Groovy:有没有比copyWith方法更好的处理@Immutable对象的方法
|
我正在寻找一种灵活的方式来“修改”(复制一些值已更改)groovy中的不可变对象.有一个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}
当谈到对不可变对象做同样的事情时,根据我的知识,最好的方法是: 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,value ->
partialCopy(object.copyWith(createMap(property,value)))
}
nextToCopy = nextToCopy.getProperties()[propertyName]
return this
}
void setProperty(String property,Object value) {
result = copyingClosure.call(nextToCopy,value)
reset()
}
private void reset() {
nextToCopy = result
copyingClosure = simplyCopy
}
}
这只是在Delivery类中添加委托方法的问题: Delivery copyOn(Closure closure) {
CopyingDelegate.copyOn(this,closure)
}
高级解释: 首先,需要注意以下代码:delivery.recipient.address.street = newStreet被解释为: >访问交付对象的收件人属性 当然,CopyingProxy类没有任何属性,因此将涉及propertyMissing方法. 因此,您可以看到它是一个propertyMissing方法调用链,通过运行setProperty终止. 基本情况 为了实现所需的功能,我们保留了两个字段:nextToCopy(在开头交付)和replicationClosure(使用@Immutable(copyWith = true)转换提供的copyWith方法初始化为简单副本). 此时,如果我们有一个像delivery.copyOn {it.id =’123′}这样的简单代码,那么根据simplyCopy和setProperty实现它将被评估为delivery.copyWith [id:’123′]. 递归步骤 现在让我们看看如何使用更多级别的复制:delivery.copyOn {it.recipient.name =’newName’}. 首先,我们将在创建CopyingProxy对象时设置nextToCopy和copyingClosure的初始值,方法与上一个示例相同. 现在让我们分析在第一次propertyMissing(String propertyName)调用期间会发生什么.因此,我们将在curried函数 – partialCopy中捕获当前的nextToCopy(传递对象),replicationClosure(基于copyWith的简单复制)和propertyName(收件人). 然后这个复制将被合并到一个闭包中 { object,value -> partialCopy(object.copyWith(createMap(property,value))) }
这成为我们新的复制关闭.在下一步中,将以Base Case部分中描述的方式调用replicationClojure. 结论 然后我们执行:delivery.recipient.copyWith [name:’newName’].然后将partialCopy应用于给我们delivery.copyWith [recipient:delivery.recipient.copyWith(name:’newName’)]的结果 所以它基本上是一个copyWith方法调用树. 最重要的是,您可以看到一些摆弄结果字段和重置功能.需要在一个闭包中支持多个作业: delivery.copyOn {
it.recipient.address.street = newStreet
it.id = 'newId'
}
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
