封装与集合
年轻的时候,我了解了面向对象范例的3个属性:
- 封装形式
- 遗产
- 多态性
在Java中,封装是通过将私有属性与访问器方法(通常称为getter和setter)一起使用来实现的。 这种封装是否正确尚有争议,不在本文讨论范围之内。 但是,当属性是一个集合(类型为java.util.Collection
, java.util.Map
及其子类型)时,使用此方法来实现封装是完全错误的。
我经常看到的代码如下:
publicclassMyBean{
privateCollectioncollection;
publicCollectiongetCollection(){
returncollection;
}
publicvoidsetCollection(Collectioncollection){
this.collection=collection;
}
}
这是我看到的最常见的代码:该设计已被Hibernate之类的ORM框架所普及。 很多时候,我提出这一点时,下一个建议就是一成不变的。
publicclassMyBean{
privateCollectioncollection;
publicMyBean(Collectioncollection){
this.collection=collection;
}
publicCollectiongetCollection(){
returncollection;
}
}
没有适当的封装
但是,对于集合来说,这没有任何变化,因为Java集合本身是可变的。 显然,在构造函数中传递对集合的引用并返回对它的引用完全不是封装。 仅当不保留也不返回任何对集合的引用时,才可以进行真正的封装。
Listlist=newArrayList();
MyBeanmybean=newMyBean(list);
list.add(newObject());// We just modified the collection outside my bean
无法使用特定的子类型
此外,我的bean可能需要自己的特定集合,例如List
或Set
。 使用以下代码段,根本不可能传递Set
。
publicclassMyBean{
privateListcollection;
publicListgetCollection(){
returncollection;
}
publicvoidsetCollection(Listcollection){
this.collection=collection;
}
}
没有选择具体的实现
作为最后的推论,使用提供的引用会阻止我们使用我们自己的(也许更高效)类型, 例如 Apache Commons FastArrayList。
实施方案
任何真正封装的起点如下:
publicclassMyBean{
privateListcollection=newArrayList();
publicMyBean(Collectioncollection){
this.collection.addAll(collection);
}
publicCollectiongetCollection(){
returnCollections.unmodifiableList(collection);
}
}
这修复了上述缺点:
- 构造函数中不传递对集合的引用,因此可以防止在对象外部进行任何后续更改
- 自由使用选定的收集实现,完全隔离-留有更改的空间
- 在getter返回的集合中没有传递对包装的集合的引用
以前的代码段不使用泛型来简化可读性,请使用它们。
翻译自: https://blog.frankel.ch/back-to-basics-encapsulating-collections/
封装与集合