Java 创建“可复制”类型的接口而不是使用“可克隆”有意义吗?
我有一段代码需要发送一个对象的副本。这是因为调用的服务(运行时库)修改发送的对象。如果下面的Java 创建“可复制”类型的接口而不是使用“可克隆”有意义吗?,java,design-patterns,Java,Design Patterns,我有一段代码需要发送一个对象的副本。这是因为调用的服务(运行时库)修改发送的对象。如果下面的doThing方法需要在ImportantObj类中设置任何字段,则此对象还需要公开setter。此实现正在等待更改,但没有合理的预期在不久的将来更改。我的解决方法是提供一个类,该类执行以下操作: public class DangerousCallWrapper<T> implements DangerousCaller<T> { public T doThing(T dat
doThing
方法需要在ImportantObj
类中设置任何字段,则此对象还需要公开setter。此实现正在等待更改,但没有合理的预期在不久的将来更改。我的解决方法是提供一个类,该类执行以下操作:
public class DangerousCallWrapper<T> implements DangerousCaller<T> {
public T doThing(T dataObject) {
T cloneOfDataObject = #Clone of dataObject
// This service modifies the cloneOfDataObject... dangerous!
Optional<T> result = service.doThing(cloneOfDataObject);
return result.orElseThrow(() -> new RuntimeException("No data object returned");
}
}
public interface DangerousCaller<T> {
/**
* Performs the functionality of the DangerousService
*/
public T doThing(T);
}
public DangerousService<T> {
public T doThing(T data) {
data.importantField = null;
data.thing = "Done!";
return data;
}
}
public static void main() {
DangerousService service = new DangerousService<ImportantObj>();
ImportantObj important = new ImportantObj().setImportantField("Password for my bank account").setThing("Undone");
service.doThing(important);
//would fail this check
assertNotNull(important.importantField);
DangerousCallWrapper wrapper = new DangerousCallWrapper<ImportantObj>();
ImportantObj important = new ImportantObj().setImportantField("Password for my bank account").setThing("Undone");
service.doThing(important);
//would not fail this check
assertNotNull(important.importantField);
}
公共类DangerousCallWrapper实现了DangerousCaller{
公共T点(T数据对象){
T cloneOfDataObject=#数据对象的克隆
//此服务修改cloneOfDataObject…危险!
可选结果=service.doThing(cloneOfDataObject);
返回result.orelsetrow(()->new RuntimeException(“未返回数据对象”);
}
}
公共接口危险呼叫方{
/**
*执行危险服务的功能
*/
公共T点餐(T);
}
公共危险服务{
公共T点(T数据){
data.importantField=null;
data.thing=“完成!”;
返回数据;
}
}
公共静态void main(){
危险服务=新的危险服务();
ImportantObj important=new ImportantObj().setImportantField(“我的银行帐户密码”).setThing(“撤消”);
服务。点滴(重要);
//这张支票会不及格吗
assertNotNull(重要.重要字段);
DangerousCallWrapper=新的DangerousCallWrapper();
ImportantObj important=new ImportantObj().setImportantField(“我的银行帐户密码”).setThing(“撤消”);
服务。点滴(重要);
//这张支票不会不及格的
assertNotNull(重要.重要字段);
}
因此,该方法的第一行就是我遇到的问题。它是一种泛型类型,因此我无法显式调用某些克隆实用程序,如Jackson或类似的程序
所以我想我应该把T extends Cloneable
添加到这个方法中……但是我打开了一个蠕虫罐,发现Cloneable
是超越禁忌的()。我还读到复制构造函数可能是处理这个问题的最佳方法……但是,我不确定如何表示使用泛型
因此,我的想法是提供一个接口Copyable
,它可以执行Cloneable
所期望的操作:公开一个方法,copy()
,该方法将创建类的新实例
这是一种可行的方法吗?要解决您的问题,您需要以多态方式复制
数据对象,如下所示:
T cloneOfDataObject = dataObject.clone();
问题是,Cloneable
没有clone()
方法,因此上面的代码无法编译
基于这一前提,创建自己的Copyable
接口来定义clone()
方法是有意义的,这样您就可以在数据对象的类上利用已经实现的clone()
方法(如果存在)。为了获得最大的效率,该接口还需要是通用的:
interface Copyable<T> {
public T clone();
}
接口可复制{
公共T克隆();
}
和类型绑定:
public class DangerousCallWrapper<T extends Copyable<T>>
implements DangerousCaller<T> {
公共类危险调用包装器
实现危险的调用程序{
Cloneable
只要你小心就可以了,你可以确保你之后的每个人都同样小心。Copyable
可以工作,并且可能不会出现Cloneable
所存在的一些问题,但它是一个非标准界面,人们也必须同样小心。另一件事是使用Serializable
,只需序列化到内存缓冲区并反序列化对象即可。但请注意,这也会产生问题。最好的方法是尽可能使对象不可变,并使用withers或类似方法执行突变。不幸的是,不可变对象/控制可变性是不可能的。由于如果执行DangerousService
,则传递的对象需要变异。使用Copyable
exposeclone()
而不是copy()
,会有什么好处?如果您的实现类已经定义了clone()
方法,则公开clone()
使这些方法可以多态访问,而copy()
则不能。