代码之家  ›  专栏  ›  技术社区  ›  Hector

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

  •  -1
  • Hector  · 技术社区  · 6 年前

    我正在将一个100%Java Android应用程序改写为100%Kotlin。 不过,我一直在尝试实现我的Java step构建器。

    我使用Java step Builder,因为它们迫使代码用户在能够执行相关RxJava进程之前提供所有必需的数据和/或函数。

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

    Java Step Builder的使用允许开发人员编写以下代码:-

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

    我想要的是这种方法的Kotlin版本。 我最初的想法是,科特林将支持建设者和Step建设者。

    我并不“珍视”在Kotlin中雇佣建设者,Kotlin解决方案必须迫使使用我的代码的开发人员在能够执行相关的“已执行”代码之前提供所有必需的数据和/或函数。

    通过对Kotlin的调查,我发现了内部DSL,它们本身听起来既有有趣的话题,也有可能解决这个特定问题。

    我有很多Step构建器要实现,其中没有一个有超过6个参数。不过,我确实喜欢尝试保持固体不超过三个参数。

    此外,如果有什么不同,那么传递的一些参数是RxJava操作和使用者。默认值在这里不相关,因为没有一个参数具有可行的默认值。

    使现代化

    我的Java step构建器都是这样的:-

    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;
            }
        }
    }
    
    0 回复  |  直到 6 年前
        1
  •  2
  •   Pablo Baxter    6 年前

    Kotlin中的step builder模式是完全可行的,我提供了一个与您提供的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项目中,step builder有点反模式。通过在语言中内置默认参数和命名参数,您实际上可以通过一个简单的数据类实现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中的step builder模式的另一个示例,您可能也想查看以下内容: https://www.baeldung.com/kotlin-builder-pattern