Как реализовать шаблон Java Step Builder в Котлине - PullRequest
0 голосов
/ 01 марта 2019

В моем текущем проекте Android я изучаю использование Kotlin.Я переписываю 100% Java-приложение для Android на 100% Kotlin.Однако я застрял при попытке реализовать мои построители шагов Java.

Я использовал построители шагов Java, поскольку они заставляют пользователей моего кода предоставлять все необходимые данные и / или функции, прежде чем они смогут выполнить связанный процесс RxJava..

Эти процессы RxJava являются сложными, и я хотел максимально упростить их инициализацию и выполнение.

Использование компоновщиков Java Step позволяет разработчикам писать следующий код: -

Sequence.builder()
    .stepOne(one)
    .stepTwo(two)
    .stepThree(three)
    .build()
    .execute();

Я ищу котлинскую версию этого подхода.Первоначально я думал, что Kotlin будет поддерживать Builders и Step Builders.

Я не «ценен» при использовании Builders в Kotlin, решение Kotlin должно заставить разработчиков, использующих мой код, предоставлять все необходимые данные и /или функции, прежде чем они смогут выполнить связанный «исполняемый» код.

Из исследования Kotlin я обнаружил внутренние DSL, которые сами по себе звучат как интересная тема, так и возможное решение этого конкретного вопроса.

У меня есть ряд Step Builder для реализации, ни один из них не имеет более 6 параметров.Однако я стараюсь придерживаться правила SOLID не более трех параметров.

Также, если это имеет какое-либо значение, некоторые из переданных параметров являются действиями и потребителями RxJava.Значения по умолчанию здесь не имеют значения, поскольку ни один из параметров не имеет допустимых значений по умолчанию.

ОБНОВЛЕНИЕ

Все мои построители шагов Java похожи на это: -

public class ExampleSequence extends Sequence {

    private static final String TAG = "ExampleSequence";

    private final Action onComplete;
    private final Consumer<? super Throwable> onError;

    /**
     * @param builder
     */
    private ExampleSequence(final Builder builder) {
        super(builder.getDoLoginRefreshFail());
        this.onError = builder.getOnError();
        this.onComplete = builder.getOnComplete();
    }

    /**
     *
     */
    public static OnCompleteAction builder() {
        return new Builder();
    }

    public interface OnCompleteAction {
        onErrorAction onComplete(@NonNull final Action onComplete);
    }

    public interface onErrorAction {
        DoLoginRefreshFail onError(@NonNull final Consumer<? super Throwable> onError);
    }

    public interface DoLoginRefreshFail {
        Build doLoginRefreshFail(@NonNull final Action doLoginRefreshFail);
    }

    public interface Build {
        ExampleSequence build();
    }

    @SuppressLint("CheckResult")
    public void execute() {
        final AtomicInteger retryCounter = new AtomicInteger(0);

        final Observable<Response<GraphqlQueryResponse>> feedArticles = getPageAndNextInboxArticles(offset, limit)
                .onErrorResumeNext(manufactureResumeNext())
                .subscribeOn(Schedulers.io());

        final Observable<Response<GraphqlQueryResponse>> readingListArticles = getPageAndReadingListArticles(readingListoffset, limit)
                .onErrorResumeNext(manufactureResumeNext())
                .subscribeOn(Schedulers.io());

        login()
                .flatMap(...)
                .ignoreElement()
                .andThen(...)
                .andThen(...)
                .ignoreElements()
                .andThen(...)
                .flattenAsObservable(x -> x)
                .flatMapCompletable(...)
                .retryWhen(errors -> errors.flatMap(e -> constructRetryHandler(retryCounter, e)))
                .doOnComplete(onComplete)
                .doOnError(onError)
                .doAfterTerminate(doAfterTerminate())
                .doOnSubscribe(compositeDisposable::add)
                .blockingAwait();
    }

    /**********************************************************************************
     *
     * BUILDER
     *
     */
    public static class Builder implements OnCompleteAction, onErrorAction, DoLoginRefreshFail, Build {

        private Action onComplete;
        private Consumer<? super Throwable> onError;
        private Action doLoginRefreshFail;

        /***********************************************************************
         *
         */
        @Override
        public ExampleSequence build() {
            return new ExampleSequence(this);
        }

        @Override
        public onErrorAction onComplete(@NonNull final Action onComplete) {
            this.onComplete = onComplete;
            return this;
        }

        @Override
        public DoLoginRefreshFail onError(@NonNull final Consumer<? super Throwable> onError) {
            this.onError = onError;
            return this;
        }


        @Override
        public Build doLoginRefreshFail(@NonNull final Action doLoginRefreshFail) {
            this.doLoginRefreshFail = doLoginRefreshFail;
            return this;
        }

        /**
         * @return the onError
         */
        Consumer<? super Throwable> getOnError() {
            return onError;
        }

        /**
         * @return the onComplete
         */
        Action getOnComplete() {
            return onComplete;
        }

        Action getDoLoginRefreshFail() {
            return doLoginRefreshFail;
        }
    }
}

1 Ответ

0 голосов
/ 03 марта 2019

Шаблон компоновщика шагов в Kotlin полностью выполним, и я привел пример, который отражает приведенный вами пример Java.

class ExampleSequence private constructor(builder: Builder): Sequence(builder.doLoginRefreshFail) { //This is your "super()" call.

    //This is equivalent to assigning the final variables [onComplete] and [onError] in the class constructor
    private val onComplete = builder.onComplete
    private val onError = builder.onError

    //More info about companion objects here: https://kotlinlang.org/docs/reference/object-declarations.html#companion-objects
    companion object {

        //Java will see this as [ExampleSequence.Companion.builder()] unless you add this annotation
        @JvmStatic
        fun builder(): OnCompleteAction = Builder()
    }


    fun execute() {
        //Do your stuff here...
    }

    //The following classes and interfaces are similar to being static inner classes. If you want the classes to access
    //fields of the enclosing outer class, you must use the keyword [inner] before declaring the class. Example:
    // inner class Foo { ... }

    interface OnCompleteAction {
        fun onComplete(onComplete: Action): onErrorAction
    }

    interface DoLoginRefreshFail {
        fun doLoginRefreshFail(doLoginRefreshFail: Action): Build
    }

    interface onErrorAction {
        fun onError(onError: Consumer<in Throwable>): DoLoginRefreshFail //The [in] keyword is the same as saying Consumer<? super Throwable>
    }

    interface Build {
        fun build(): ExampleSequence
    }

    class Builder: OnCompleteAction, onErrorAction, DoLoginRefreshFail, Build {

        //The [lateinit] keyword states that this variable will be initialized later. Calling it before it is initialized will throw an exception
        lateinit var onComplete: Action
            private set //Only this class can modify.

        lateinit var onError: Consumer<in Throwable>
            private set

        lateinit var doLoginRefreshFail: Action
            private set

        //No special differences here... oooh, inlined [override] keyword!
        override fun onComplete(onComplete: Action): onErrorAction {
            this.onComplete = onComplete
            return this
        }

        override fun doLoginRefreshFail(doLoginRefreshFail: Action): Build {
            this.doLoginRefreshFail = doLoginRefreshFail
            return this
        }

        override fun onError(onError: Consumer<in Throwable>): DoLoginRefreshFail {
            this.onError = onError
            return this
        }

        override fun build(): ExampleSequence = ExampleSequence(this)

        //Where are the getter methods? If you look at the variable declarations, they are public by default.
        //This means that these variables are public read, but can only be set by this class only. In other words, built-in getter!
    }
}

Однако в чистом проекте Kotlin компоновщик шаговсвоего рода анти-паттерн.С параметрами по умолчанию и именованными параметрами, встроенными в язык, вы на самом деле можете получить SOLID с помощью простого класса данных.Взяв, например, класс ExampleSequence, ваше решение может выглядеть примерно так:

data class ExampleSequence(
        private val onComplete: Action,
        private val onError: Consumer<in Throwable>,
        private val doLoginRefreshFail: Action,
        private val aNewParam: String = "Default")
    : Sequence(doLoginRefreshFail) { //This is your "super()" call.

    fun execute() {
        //Do your stuff here...
    }
}

fun foo() {
    //Example of using named parameters and passing in variables. Notice parameters aren't in the same order as how it is declared in the class
    ExampleSequence(
            onError = Consumer(),
            onComplete = Action(),
            doLoginRefreshFail = Action()
    ).execute()

    //Since I added [aNewParam], instead of using the default, let's change it.
    ExampleSequence(
            onError = Consumer(),
            onComplete = Action(),
            doLoginRefreshFail = Action(),
            aNewParam = "Something else!"
    ).execute()
}

Вот хорошая статья, которая будет немного подробнее: https://dev.to/chrisvasqm/avoiding-the-builder-design-pattern-in-kotlin-3b1a

Кроме того, на случайвам нужен еще один пример шаблона построения ступеней в Kotlin, вы также можете проверить это: https://www.baeldung.com/kotlin-builder-pattern

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...