gpt4 book ai didi

Groovy:有没有比 copyWith 方法更好的处理@Immutable 对象的方法

转载 作者:行者123 更新时间:2023-12-03 15:00:13 24 4
gpt4 key购买 nike

我正在寻找一种灵活的方式在 groovy 中“修改”(复制某些值已更改)不可变对象(immutable对象)。有一个 copyWith 方法,但它只允许您替换对象的某些属性。好像还不够方便。

假设我们有一组代表某个系统的领域设计的类:

@Immutable(copyWith = true)
class Delivery {
String id
Person recipient
List<Item> items
}

@Immutable(copyWith = true)
class Person {
String name
Address address
}

@Immutable(copyWith = true)
class Address {
String street
String postalCode
}

假设我需要更改收货人的街道。在常规可变对象的情况下,执行以下操作就可以了:
delivery.recipient.address.street = newStreet

或(在某些情况下可能有用):
delivery.with {recipient.address.street = newStreet}

当涉及到对不可变对象(immutable对象)做同样的事情时,据我所知,最好的方法是:
def recipient = delivery.recipient
def address = recipient.address
delivery.copyWith(recipient:
recipient.copyWith(address:
address.copyWith(street: newStreet)))

Spock 集成测试代码实际上需要它,因此可读性和表达性很重要。上面的版本不能“即时”使用,所以为了避免创建大量的辅助方法,我已经实现了我自己的 copyOn (因为采用了 copyWith )方法,以便可以编写:

def deliveryWithNewStreet = delivery.copyOn { it.recipient.address.street = newStreet }

但是,我想知道是否有最终解决方案,存在于 groovy 中或由某些外部库提供。谢谢

最佳答案

为了完整起见,我提供了 copyOn 方法的实现。它是这样的:

class CopyingDelegate {
static <T> T copyOn(T source, Closure closure) {
def copyingProxy = new CopyingProxy(source)
closure.call(copyingProxy)
return (T) copyingProxy.result
}
}

class CopyingProxy {
private Object nextToCopy
private Object result
private Closure copyingClosure

private final Closure simplyCopy = { instance, property, value -> instance.copyWith(createMap(property, value)) }
private final def createMap = { property, value -> def map = [:]; map.put(property, value); map }

CopyingProxy(Object nextToCopy) {
this.nextToCopy = nextToCopy
copyingClosure = simplyCopy
}

def propertyMissing(String propertyName) {
def partialCopy = copyingClosure.curry(nextToCopy, propertyName)
copyingClosure = { object, property, value ->
partialCopy(object.copyWith(createMap(property, value)))
}
nextToCopy = nextToCopy.getProperties()[propertyName]
return this
}

void setProperty(String property, Object value) {
result = copyingClosure.call(nextToCopy, property, value)
reset()
}

private void reset() {
nextToCopy = result
copyingClosure = simplyCopy
}
}

然后只需在 Delivery 类中添加委托(delegate)方法即可:
Delivery copyOn(Closure closure) {
CopyingDelegate.copyOn(this, closure)
}

高层解释:

首先需要注意的是代码: delivery.recipient.address.street = newStreet被解释为:
  • 访问 recipient delivery 的属性(property)对象
  • 访问 address上面的结果是什么
  • 分配属性 street值为 newStreet

  • 当然上课 CopyingProxy没有任何这些属性,所以 propertyMissing会涉及到方法。

    如您所见,它是 propertyMissing 的链通过运行 setProperty 终止方法调用.

    基本情况

    为了实现所需的功能,我们维护两个字段: nextToCopy (开头是交付)和 copyingClosure (使用 copyWith 转换提供的 @Immutable(copyWith = true) 方法初始化为简单副本)。

    此时,如果我们有一个简单的代码,如 delivery.copyOn { it.id = '123' }那么它将被评估为 delivery.copyWith [id:'123']根据 simplyCopysetProperty实现。

    递归步骤

    现在让我们看看它如何在多一层复制下工作: delivery.copyOn { it.recipient.name = 'newName' } .

    首先,我们将设置 nextToCopy 的初始值。和 copyingClosure在创建 CopyingProxy 时对象的方式与前面的示例相同。

    现在让我们分析在第一个 propertyMissing(String propertyName) 期间会发生什么。称呼。所以我们将捕获当前 nextToCopy (交付对象), copyingClosure (基于 copyWith 的简单复制)和 propertyName (收件人)在一个柯里化(Currying)函数中 - partialCopy .

    然后这个复制将被合并到一个闭包中
    { object, property, value -> partialCopy(object.copyWith(createMap(property, value))) }

    这成为我们的新 copyingClosure .在下一步中,此 copyingClojure 中描述的方式调用基础案例 部分。

    结论

    然后我们执行了: delivery.recipient.copyWith [name:'newName'] .然后是 partialCopy应用于给我们 delivery.copyWith[recipient:delivery.recipient.copyWith(name:'newName')] 的结果

    所以它基本上是 copyWith 的树方法调用。

    最重要的是,你可以看到一些摆弄 result字段和 reset功能。需要在一个闭包中支持多个任务:
    delivery.copyOn { 
    it.recipient.address.street = newStreet
    it.id = 'newId'
    }

    关于Groovy:有没有比 copyWith 方法更好的处理@Immutable 对象的方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35938713/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com