在我当前的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;
}
}
}
答案 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