如何在Kotlin中实现Java step Builder模式

时间:2019-03-01 06:31:02

标签: android design-patterns kotlin

在我当前的Android项目中,我正在研究Kotlin的使用。 我正在将100%Java Android应用重写为100%Kotlin。 不过,我一直坚持尝试实现Java步骤构建器。

我使用Java步骤生成器是因为它们迫使我的代码的用户在执行关联的RxJava进程之前提供所有必需的数据和/或功能。

这些RxJava进程很复杂,我希望尽可能简化它们的初始化和执行。

使用Java步骤构建器允许开发人员编写以下代码:-

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

我正在寻找的是这种方法的Kotlin版本。 我最初的想法是Kotlin将支持Builders和Step Builders。

我对在Kotlin中使用Builders并不“珍惜”,Kotlin解决方案必须强制使用我的代码的开发人员提供所有必需的数据和/或功能,然后才能执行相关的“已执行”的代码。 / p>

通过调查Kotlin,我发现了内部DSL,它们本身听起来很有趣,而且可以解决这个特定问题。

我有许多要实现的步骤构建器,这些都没有超过6个参数。我确实想尝试并保持SOLID不超过三个参数的规则。

如果有什么不同,传递的一些参数是RxJava Actions和Consumers。默认值与此处无关,因为所有参数都没有可行的默认值。

更新

我的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 :(得分:2)

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