通过一系列连续的多米诺骨牌操作来管理对象的最佳方法是什么?

dav*_*ari 2 kotlin

假设我们有一个Order类和一个方法,其中一些服务执行一系列操作

fun doStuff(order: Order): Order {

val orderByServiceA = serviceA.operationA(order: Order)
val orderByServiceB = serviceB.operationB(orderByServiceA: Order)
val orderByServiceC = serviceC.operationC(orderByServiceB: Order)
return orderByServiceC
}
Run Code Online (Sandbox Code Playgroud)

我创建了一个可以由服务实现的通用接口

interface IOrderService {
    fun operation(order: Order): Order
}
Run Code Online (Sandbox Code Playgroud)

于是上面的方法就变成了

fun doStuff(order: Order): Order {

val orderByServiceA = serviceA.operation(order: Order)
val orderByServiceB = serviceB.operation(orderByServiceA: Order)
val orderByServiceC = serviceC.operation(orderByServiceB: Order)
return orderByServiceC
}
Run Code Online (Sandbox Code Playgroud)

此代码中有某种重复,因此下一步是使此函数对扩展开放并对修改关闭。我将IOrderService的所有实例添加到列表中,因此函数会发生如下变化

fun doStuff(order: Order): Order {

orderServices.forEach { service ->
service.operation(order)
}
...
}
Run Code Online (Sandbox Code Playgroud)

这个解决方案非常好,因为如果我想添加另一个实现操作功能的服务,这是很容易做到的。

但是有一个问题..最后一个解决方案中传递的订单对象对于所有服务都是相同的,因此建议的解决方案与第一个实现不同。每个服务都需要根据之前的结果执行操作功能

所以我在这一点上陷入了困境,因为我想找到一个干净的解决方案..我正在考虑一些设计模式,例如观察者(但它是一个一对多的解决方案,但事实并非如此)纪念品模式(但我可能会继续有一个连续的操作列表),发布订阅......但我认为这些都不是正确的选择。它就像多米诺骨牌,其中对象传递给第一个服务,结果传递给第二个服务,等等,当然还有 doStuff 方法返回的最终结果

有什么建议来完成最后一段代码吗?多谢!

bro*_*oot 6

我认为你真的把所有这些模式搞得太复杂了。你所描述的是......只是一个简单的循环!

fun doStuff(order: Order): Order {
    var curr = order
    for (service in orderServices) {
        curr = service.operation(curr)
    }
    return curr
}
Run Code Online (Sandbox Code Playgroud)

如果您喜欢函数式编程,那么可以通过减少/折叠服务列表来更简单地解决它:

fun doStuff(order: Order): Order {
    return orderServices.fold(order) { o, svc -> svc.operation(o) }
}
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请参阅文档fold()https://kotlinlang.org/docs/collection-aggregate.html#fold-and-reduce


cac*_*acs 5

@broot\'s 答案涵盖了执行此操作的一般方法\xe2\x80\x94就像他们所说的那样,是在迭代某些内容时累积fold结果的基本操作\xe2\x80\x94但是如果你想明确地写出来,你可以链接你的电话:

\n
fun doStuff(order: Order) = \n    order.let { serviceA.operationA(it) }\n        .let { serviceB.operationB(it) }\n        .let { serviceC.operationC(it) }\n
Run Code Online (Sandbox Code Playgroud)\n

如果您使用函数引用,其中参数或接收器自动传递给您正在引用的函数,您可以这样做:

\n
fun doStuff(order: Order) = \n    order.let(serviceA::operationA)\n        .let(serviceB::operationB)\n        .let(serviceC::operationC)\n
Run Code Online (Sandbox Code Playgroud)\n

因此,您最终会得到这个非常清晰的操作管道,原始操作Order正在逐步传递。

\n

另一个好处是你的类型不需要保持一致:operationB可以从 中获取完全不同的参数operationA,重要的是出来的类型operationA与进入 的类型相同operationB。而使用折叠时,累加器\xe2\x80\x94(从一个步骤传递到下一步的值 \xe2\x80\x94)是一种固定类型,并且您正在迭代的函数集合也具有固定类型;在这种情况下,他们都需要获取Order并返回Order。它们都很有用,并且您可以选择\xe2\x80\x94,使用适合情况的任何内容!

\n