在聚合 class 中使用克隆的最佳实践

The best practice using clone in an aggregated class

我有一个系统,其中有一个客户,其属性为补充剂的 ArrayList。

客户 class 的代码是:

public class Customer implements Cloneable
{
         .
         .
         ArrayList<Supplement> suppList
    public Customer(String fName, String lName, String emailInput, ArrayList<Supplement> list)
    {
        setFName(fName);
        setLName(lName);
        setEmailAddr(emailInput);
        setSuppList(list);
    }
   public void setSuppList(ArrayList<Supplement> list)
    {
        suppList = new ArrayList<Supplement>();
        for(Supplement sp : list)
        {
            suppList.add(sp);
        }
    }
}
 public ArrayList<Supplement> getSuppList() throws CloneNotSupportedException
    {
        ArrayList<Supplement> list = new ArrayList<Supplement>();
        if (suppList != null)
        {
            for(Supplement sp : suppList)
            {
                list.add((Supplement)sp.clone());
            }
        }
        return list;
    }
 public void addSupp(Supplement item)
    {
        suppList.add(item);
    }

    public void removeSupp(Supplement item)
    {
        suppList.remove(item);
    }

最初,我的 setSuppList 方法只包含一行代码,即 suppList = list,而我的 getSuppList 方法只是 'return suppList'。 我觉得这是一种隐私泄露,所以我在这两种方法上都调用了克隆。 对于 setSuppList,suppList = new ArrayList() 并遍历参数列表并克隆每个对象并将其添加到 suppList 数组中。 对于 getSuppList,它遍历 suppList 并克隆其中的每个 Supplement 对象,将其添加到新数组和 return 数组中。 但是,我意识到如果将补充对象价格从 3 美元更改为 50 美元,并且如果我有 100 个客户,则意味着我必须继续调用 setSuppList() 100x。

我改变了主意,所以我将 setSuppList 方法更改为 suppList = list 并将克隆内容仅保留在 getSuppList 中。

然后我想到了...为什么不将 suppList 作为 setSuppList 中的新数组并将参数 'list' 中的每个项目添加到 suppList。 这样,list 和 suppList 都引用相同的对象 但是当列表删除一个项目时,Customer 对象的 suppList 不受影响。 只是当单个项目受到影响时,suppList 相应地更新(例如补充剂的价格) 当我想在 suppList 中添加或删除一个项目时,我可以使用 addSupp 方法或 removeSupp 方法,或 setSuppList 方法。

public static void main(String[] args)
    {
        try
        {
            Supplement s1 = new Supplement("A", 2.9);
            Supplement s2 = new Supplement("B", 3);
            ArrayList<Supplement> spList = new ArrayList<Supplement>();
            spList.add(s1);
            spList.add(s2);
            Customer cstmr = new Customer("killua", "zoldyck", "killua@gmail.com", spList);



ArrayList<Supplement> spList2 = cstmr.getSuppList();
        System.out.println("cloned array size : "+spList2.size());
        spList2.remove(0);
        System.out.println("After removing an item in the cloned array");
        System.out.println("cloned array size : "+spList2.size());
        System.out.println("Array returned from getSuppList size : "+cstmr.getSuppList().size());

    spList.remove(0);
    System.out.println("array size : "+cstmr.getSuppList().size());
    System.out.println("");

    spList.get(0).setWeeklyCost(50);
    System.out.println("If I change the first item in the first array prize to 50");
    System.out.println("price of 1st item in object in the first array : "+spList.get(0).getWeeklyCost());
    System.out.println("price of 1st item in object in the array returned by getSuppList : "+cstmr.getSuppList().get(0).getWeeklyCost());
    System.out.println("");

    s1.setWeeklyCost(40);
    System.out.println("If I change the supplement object directly to 40");
    System.out.println("price of 1st item in object in the first array : "+spList.get(0).getWeeklyCost());
    System.out.println("price of 1st item in object in the array returned by getSuppList : "+cstmr.getSuppList().get(0).getWeeklyCost());

}
catch(CloneNotSupportedException e)
{

}

}

输出

克隆数组大小:2

删除克隆数组中的项目后

克隆数组大小:1

数组 return 来自 getSuppList 大小:2

数组大小:2

如果我将第一个数组奖品中的第一项更改为 50

第一个数组中对象中第一项的价格:50.0

数组中对象中第一项的价格 return由 getSuppList 编辑:2.9

如果我直接把补充对象改成40

第一个数组中对象中第一项的价格:50.0

数组中对象中第一项的价格 return由 getSuppList 编辑:40.0

但是,当我尝试这样做时,出现了意外行为。 spList.get(0).setWeeklyCost(50);不影响客户对象的 suppList 对象。

怎么可能? spList 和 suppList 都引用相同的对象,尽管它们是不同的数组...

由于 Supplement 是它自己的东西,与 Customer 完全不同,并且可以自行重命名和重新定价,因此您永远不应将其克隆为 Customer 逻辑的一部分。

你应该 "clone" list,因为那是一个 属性 的客户,这很容易通过调用 copy-constructor :

suppList = new ArrayList<>(list);

spList.get(0).setWeeklyCost(50); does not affect the suppList object of a Customer object. How can it be ??

因为当你调用cstmr.getSuppList()

时你还在克隆Supplement