clone - 封装聚合/组合

标签 clone encapsulation aggregation composition

维基百科关于封装状态的文章:

“封装还可以防止用户将组件的内部数据设置为无效或不一致的状态,从而保护组件的完整性”

我在论坛上开始了关于封装的讨论,其中我询问是否应该始终在 setter 和/或 getter 内克隆对象,以保留上述封装规则。我认为,如果您想确保主对象内部的对象不会在主对象外部被篡改,您应该始终克隆它。

一位讨论者认为,在这个问题上应该区分聚合和组合。基本上我认为他的意思是这样的:

  • 如果您想返回属于组合一部分的对象(例如,矩形的点),请克隆它。
  • 如果您想返回属于聚合一部分的对象(例如,作为 UserManager 一部分的 User),只需在不破坏引用的情况下返回它即可。

这对我来说也很有意义。但现在我有点困惑了。并想听听您对此事的看法。

严格来说,封装是否总是要求克隆?

PS:我用 PHP 编程,其中资源管理可能更相关,因为它是一种脚本语言。

最佳答案

Strictly speaking, does encapulation always mandate cloning?

不,事实并非如此。

您提到的人可能混淆了对象状态的保护与对象实现细节的保护。

请记住这一点:封装是一种提高代码灵活性的技术。封装良好的类可以更改其实现而不影响其客户端。这就是封装的本质。

假设有以下类:

class PayRoll {
   private List<Employee> employees;

   public void addEmployee(Employee employee) {
       this.employees.add(employee);
   }
   public List<Employee> getEmployees() {
       return this.employees;
   }
}

现在,此类的封装性较低。您可以说方法 getEmployees 破坏了封装,因为通过返回类型 List,您无法再在不影响类的客户端的情况下更改此实现细节。例如,我无法在不影响客户端代码的情况下更改它(例如 map 集合)。

通过克隆对象的状态,您可能会更改客户端的预期行为。这是解释封装的有害方式。

public List<Employee> getEmployees() {
    return this.employees.clone();
}

可以说上面的代码改进了封装,因为现在 addEmployee 是唯一可以修改内部 List 的地方。因此,如果我有一个设计决定,将新的员工项目添加到列表的头部而不是尾部。我可以做这样的修改:

public void addEmployee(Employee employee) {
    this.employees.insert(employee);  //note "insert" is used instead of "add"
}

然而,这只是封装的一小部分增量,但代价却很大。您的客户给人的印象是可以接触到员工,但实际上他们只有一份副本。因此,如果我想更新员工 John Doe 的电话号码,我可能会错误地访问 Employee 对象,并期望在下次调用 PayRoll.getEmployees 时反射(reflect)更改。

具有更高封装性的实现将执行以下操作:

class PayRoll {
   private List<Employee> employees;

   public void addEmployee(Employee employee) {
       this.employees.add(employee);
   }
   public Employee getEmployee(int position) {
       return this.employees.get(position);
   }
   public int count() {
       return this.employees.size();
   }
}

现在,如果我想更改 map 列表,我可以自由地进行。 此外,我并没有破坏客户可能期望的行为:当从 PayRoll 修改 Employee 对象时,这些修改不会丢失。

我不想过多地扩展自己,但请告诉我这是否清楚。我很乐意继续提供更详细的示例。

关于clone - 封装聚合/组合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1349142/

相关文章:

pandas - 如何指示 Pandas、Python 中分类列中值的计数?

Java:当您只能访问接口(interface)时复制对象

克隆系统调用 OS X 未链接 - undefined symbol

generics - 如何在不使用克隆的情况下编写保持状态并返回值的通用迭代器?

PHP:封装的目的是什么?

Python函数封装(以避免在没有适当上下文的情况下意外调用)

MySQL聚合

git-update-server-info 不足以克隆远程仓库?

java - 状态模式和封装

r - 用 R 有条件计算列中值的数量