博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
第16条:复合优先于继承
阅读量:7064 次
发布时间:2019-06-28

本文共 3604 字,大约阅读时间需要 12 分钟。

继承是实现代码重用的有力手段,但是使用不当会导致软件变得脆弱。在包的内部使用继承是非常安全的,子类和超类的实现都处在同一个程序员的控制之下。对于专门为了继承而设计、并且具有很好的文档说明的类来说,使用继承也是非常安全的。然而们对于进行跨越包边界的继承,则要非常小心。“继承”在这里特指一个类扩展另一个类。

 

public class InstrumentedHashSet
extends HashSet
{ private int addCount = 0; public InstrumentedHashSet() { } public InstrumentedHashSet(int initCap, float loadFactor) { super(initCap, loadFactor); } @Override public boolean add(E e) { addCount++; return super.add(e); } @Override public boolean addAll(Collection
c) { addCount += c.size(); return super.addAll(c); } public int getAddCount() { return addCount; } public static void main(String[] args) { InstrumentedHashSet
s = new InstrumentedHashSet
(); s.addAll(Arrays.asList("s","a","p")); System.out.println(s.getAddCount()); }}

考虑上面的提供计数功能的HashSet。我们希望得到的结果是3,但结果却是6,原因是addAll方法是基于add方法实现的,InstrumentedHashSet的addAll方法首先将addCount加3,然后super.addAll调用HashSet的addAll方法,而addAll又分别调用到InstrumentedHashSet覆盖了的add方法,每个元素调用一次,又给addCount加了3,结果是6。

只要去掉覆盖的addAll方法就可以修正问题,虽然这个类可以正常工作了,但是HashSet的addAll方法是实现细节,不是承诺,意味着不能保证addAll的实现方法在未来发行的版本保持不变。

 

稍微好一点的方法是覆盖addAll方法来遍历指定的集合,为每个元素调用一次add方法,这样可以保证得到正确的结果,然而这种方法对于要访问超类的私有域就无能为力了。

覆盖超类的每一个添加元素的方法,如果超类以后增加一种新的添加元素方法,那就可能有“非法的元素”添加到集合中。

 

不扩展现有的类,而是在新的类中增加一个私有域,它引用现有类的一个实例,可以避免前面提到的问题,这种设计称为“复合”,因为现有的类变成了新类的一个组件,新类中的每个实例方法都可以调用被包含的现有类实例中对应的方法,并返回它的结果。

public class ForwardingSet
implements Set
{ private Set
s; public ForwardingSet(Set
s) { this.s = s; } public void clear() { s.clear(); } public boolean contain(Object o) { return s.contains(o); } public boolean isEmpty() { return s.isEmpty(); } public int size() { return s.size(); } public Iterator
iterator() { return s.iterator(); } public boolean add(E e) { return s.add(e); } public boolean remove(Object o) { return s.remove(o); } public boolean containsAll(Collection
c) { return s.containsAll(c); } public boolean addAll(Collection
c) { return s.addAll(c); } public boolean retainAll(Collection
c) { return s.retainAll(c); } public Object[] toArray() { return s.toArray(); } public boolean equals(Object o) { return s.equals(o); } public int hashCode() { return s.hashCode(); } public String toString() { return s.toString(); } @Override public boolean contains(Object o) { // TODO Auto-generated method stub return false; } @Override public
T[] toArray(T[] a) { // TODO Auto-generated method stub return null; } @Override public boolean removeAll(Collection
c) { return s.removeAll(c); } }

 

public class InstrumentedSet
extends ForwardingSet
{ private int addCount = 0; public InstrumentedSet(Set
s) { super(s); } public boolean add(E e) { addCount++; return super.add(e); } public boolean addAll(Collection
c) { addCount += c.size(); return super.addAll(c); } public int getAddCount() { return addCount; }}

ForwardingSet作为一个转发类,拥有一个Set实例变量,而InstrumentedSet把add和addAll方法覆盖掉,实现计数功能。

转载于:https://www.cnblogs.com/13jhzeng/p/5682111.html

你可能感兴趣的文章
求数组中只出现一次的数字(算法)
查看>>
OpenCV 2.4+ C++ SVM文字识别
查看>>
2012年最受欢迎的PHP框架
查看>>
Android滑动手势侦测方法介绍
查看>>
ECMALL综合手册
查看>>
QQ开放API
查看>>
#、%和$符号在OGNL表达式中经常出现
查看>>
仪表运算放大器INA333
查看>>
快速理解URL重写
查看>>
关于spring和ibatis的整合
查看>>
Oracle Apps AutoConfig
查看>>
[leetcode]Flatten Binary Tree to Linked List
查看>>
css颜色代码大全:(网页设计师和平面设计师常用)
查看>>
boost 1.52在windows下的配置
查看>>
素材锦囊——50个高质量的 PSD 素材免费下载《上篇》
查看>>
【转】oc中消息传递机制-附:对performSelector方法的扩充
查看>>
oracle的nvl和sql server的isnull
查看>>
[转]虚拟机下Ubuntu共享主机文件(Ubuntu、VMware、共享)
查看>>
高血压 治疗 偏方
查看>>
HtmlAttribute HTML属性处理类
查看>>