厳密に言えば、カプセル化は常にクローン作成を義務付けますか?
いいえ、違います。
あなたが言及した人は、オブジェクトの状態の保護とオブジェクトの実装の詳細の保護を混同している可能性があります。
これを覚えておいてください: カプセル化は、コードの柔軟性を高めるための手法です。適切にカプセル化されたクラスは、クライアントに影響を与えることなく実装を変更できます。これがカプセル化の本質です。
次のクラスがあるとします。
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 の電話番号を更新したい場合、PayRoll.getEmployees への次の呼び出しで変更が反映されることを期待して、誤って Employee オブジェクトにアクセスする可能性があります。
より高度なカプセル化による実装は、次のようになります。
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();
}
}
マップのリストを変更したい場合は、自由に変更できます。さらに、クライアントがおそらく期待している動作を壊しているわけではありません。PayRoll から Employee オブジェクトを変更しても、これらの変更は失われません。
あまり拡張したくありませんが、これが明確かどうか教えてください。より詳細な例に進んでいただければ幸いです。