Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 创建“可复制”类型的接口而不是使用“可克隆”有意义吗?_Java_Design Patterns - Fatal编程技术网

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
expose
clone()
而不是
copy()
,会有什么好处?如果您的实现类已经定义了
clone()
方法,则公开
clone()
使这些方法可以多态访问,而
copy()
则不能。