Реализация пользовательских операторов в RxJava

Реализация пользовательских операторов в RxJava

1. обзор

В этом кратком руководстве мы покажем, как написать собственный оператор с помощьюRxJava.

Мы обсудим, как создать этот простой оператор, а также преобразователь - как класс, так и простую функцию.

2. Конфигурация Maven

Во-первых, нам нужно убедиться, что у нас есть зависимостьrxjava вpom.xml:


    io.reactivex
    rxjava
    1.3.0

Мы можем проверить последнюю версиюrxjava наMaven Central.

3. Пользовательский оператор

We can create our custom operator by implementing Operator interface, в следующем примере мы реализовали простой оператор для удаления не буквенно-цифровых символов изString:

public class ToCleanString implements Operator {

    public static ToCleanString toCleanString() {
        return new ToCleanString();
    }

    private ToCleanString() {
        super();
    }

    @Override
    public Subscriber call(final Subscriber subscriber) {
        return new Subscriber(subscriber) {
            @Override
            public void onCompleted() {
                if (!subscriber.isUnsubscribed()) {
                    subscriber.onCompleted();
                }
            }

            @Override
            public void onError(Throwable t) {
                if (!subscriber.isUnsubscribed()) {
                    subscriber.onError(t);
                }
            }

            @Override
            public void onNext(String item) {
                if (!subscriber.isUnsubscribed()) {
                    final String result = item.replaceAll("[^A-Za-z0-9]", "");
                    subscriber.onNext(result);
                }
            }
        };
    }
}

В приведенном выше примере нам нужно проверить, подписан ли подписчик, прежде чем применять нашу операцию и отправлять элемент, так как это будет ненужным.

We are also restricting instance creation only to static factory methods to achieve a more user-friendly readability при связывании методов и использовании статического импорта.

И теперь мы можем использовать операторlift, чтобы легко связать наш пользовательский оператор с другими операторами:

observable.lift(toCleanString())....

Вот простой тест нашего пользовательского оператора:

@Test
public void whenUseCleanStringOperator_thenSuccess() {
    List list = Arrays.asList("john_1", "tom-3");
    List results = new ArrayList<>();
    Observable observable = Observable
      .from(list)
      .lift(toCleanString());
    observable.subscribe(results::add);

    assertThat(results, notNullValue());
    assertThat(results, hasSize(2));
    assertThat(results, hasItems("john1", "tom3"));
}

4. Трансформатор

Мы также можем создать нашего оператора, реализовав интерфейсTransformer:

public class ToLength implements Transformer {

    public static ToLength toLength() {
        return new ToLength();
    }

    private ToLength() {
        super();
    }

    @Override
    public Observable call(Observable source) {
        return source.map(String::length);
    }
}

Обратите внимание, что мы используем преобразовательtoLength, чтобы преобразовать нашу наблюдаемую изString в ее длину вInteger.

Нам понадобится операторcompose для использования нашего трансформатора:

observable.compose(toLength())...

Вот простой тест:

@Test
public void whenUseToLengthOperator_thenSuccess() {
    List list = Arrays.asList("john", "tom");
    List results = new ArrayList<>();
    Observable observable = Observable
      .from(list)
      .compose(toLength());
    observable.subscribe(results::add);

    assertThat(results, notNullValue());
    assertThat(results, hasSize(2));
    assertThat(results, hasItems(4, 3));
}

lift(Operator) работает с подписчиками наблюдаемого, аcompose(Transformer) работает с самим наблюдаемым.

Когда мы создаем собственный оператор, мы должны выбратьTransformer, если мы хотим работать с наблюдаемым объектом в целом, и выбратьOperator, если мы хотим работать с элементами, испускаемыми наблюдаемым объектом.

5. Пользовательский оператор как функция

Мы можем реализовать наш пользовательский оператор как функцию вместоpublic class:

Operator cleanStringFn = subscriber -> {
    return new Subscriber(subscriber) {
        @Override
        public void onCompleted() {
            if (!subscriber.isUnsubscribed()) {
                subscriber.onCompleted();
            }
        }

        @Override
        public void onError(Throwable t) {
            if (!subscriber.isUnsubscribed()) {
                subscriber.onError(t);
            }
        }

        @Override
        public void onNext(String str) {
            if (!subscriber.isUnsubscribed()) {
                String result = str.replaceAll("[^A-Za-z0-9]", "");
                subscriber.onNext(result);
            }
        }
    };
};

А вот простой тест:

List results = new ArrayList<>();
Observable.from(Arrays.asList("[email protected]", "or-an?ge"))
  .lift(cleanStringFn)
  .subscribe(results::add);

assertThat(results, notNullValue());
assertThat(results, hasSize(2));
assertThat(results, hasItems("apple", "orange"));

Аналогично для примераTransformer:

@Test
public void whenUseFunctionTransformer_thenSuccess() {
    Transformer toLengthFn = s -> s.map(String::length);

    List results = new ArrayList<>();
    Observable.from(Arrays.asList("apple", "orange"))
      .compose(toLengthFn)
      .subscribe(results::add);

    assertThat(results, notNullValue());
    assertThat(results, hasSize(2));
    assertThat(results, hasItems(5, 6));
}

6. Заключение

В этой статье мы показали, как писать наши операторы RxJava.

И, как всегда, полный исходный код можно найтиover on GitHub.