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' } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |