Groovy:有没有比copyWith方法更好的方法来处理@Immutable对象
我正在寻找一种灵活的方式,在groovy中“修改”(复制一些更改的值)不可变对象。有一个copyWith方法,但它只允许您替换对象的某些属性。这似乎不够方便 假设我们有一组类代表某个系统的域设计:Groovy:有没有比copyWith方法更好的方法来处理@Immutable对象,groovy,immutability,Groovy,Immutability,我正在寻找一种灵活的方式,在groovy中“修改”(复制一些更改的值)不可变对象。有一个copyWith方法,但它只允许您替换对象的某些属性。这似乎不够方便 假设我们有一组类代表某个系统的域设计: @Immutable(copyWith = true) class Delivery { String id Person recipient List<Item> items } @Immutable(copyWith = true) class Person
@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
}
或者(在某些情况下可能有用):
就我所知,在对不可变对象执行相同操作时,最好的方法是:
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.recipient.address.street=newStreet
的代码被解释为:
delivery
对象的recipient
属性地址
street
CopyingProxy
没有任何这些属性,因此将涉及propertyMissing
方法
如您所见,这是一个属性missing
方法调用链,通过运行setProperty
终止
基本情况
为了实现所需的功能,我们维护两个字段:nextToCopy
(在开始时交付)和copyingClosure
(使用@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(stringpropertyname)
调用期间会发生什么。因此,我们将在当前函数中捕获当前的nextToCopy
(传递对象)、copyingClosure
(基于copyWith的简单复制)和propertyName
(收件人)
然后,该副本将合并到一个闭包中
{ 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')]的结果
因此,它基本上是一个带有方法调用的copy树
除此之外,您还可以看到一些对结果
字段和重置
功能的摆弄。要求在一次关闭中支持多个任务:
delivery.copyOn {
it.recipient.address.street = newStreet
it.id = 'newId'
}
您的copyOn实施有什么问题?
Delivery copyOn(Closure closure) {
CopyingDelegate.copyOn(this, closure)
}
{ object, property, value -> partialCopy(object.copyWith(createMap(property, value))) }
delivery.copyOn {
it.recipient.address.street = newStreet
it.id = 'newId'
}