public class CompositionOverInheritance {
public static void main(String[] args) {
InstrumentedSet<String> st = new InstrumentedSet<String>(new HashSet<String>());
InstrumentedSet<String> st1 = new InstrumentedSet<String>(new HashSet<String>());
st.addAll(Arrays.asList("chen", "chenzq", "jaeson"));
st1.addAll(Arrays.asList("chen", "chenzq", "jaeson"));
System.out.println(st.getAddCount());
System.out.println(st.equals(st1));
//使用继承导致的计数错误
System.out.println("implements by inheritance......");
InstrumentedHashSet<String> hs = new InstrumentedHashSet<String>();
InstrumentedHashSet<String> hs1 = new InstrumentedHashSet<String>();
hs.addAll(Arrays.asList("chen", "chenzq", "jaeson"));
hs1.addAll(Arrays.asList("chen", "chenzq", "jaeson"));
System.out.println(hs.getAddCount());
System.out.println(hs1.equals(st1));
}
}
//增加集合计数器
class InstrumentedSet<E> extends ForwardingSet<E> {
private int addCount = 0;
public InstrumentedSet(Set<E> s) {
super(s);
}
@Override public boolean add(E e) {
addCount++;
return super.add(e);
}
@Override public boolean addAll(Collection<? extends E> c) {
addCount += c.size();
return super.addAll(c);
}
public int getAddCount() {
return addCount;
}
}
class ForwardingSet<E> implements Set<E> {
private final Set<E> s;
public ForwardingSet(Set<E> s) { this.s = s; }
public void clear() { s.clear(); }
public boolean contains(Object o) { return s.contains(o); }
public boolean isEmpty() { return s.isEmpty(); }
public int size() { return s.size(); }
public Iterator<E> 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<? extends E> c) { return s.addAll(c); }
public boolean removeAll(Collection<?> c) { return s.removeAll(c); }
public boolean retainAll(Collection<?> c) { return s.retainAll(c); }
public Object[] toArray() { return s.toArray(); }
public <T> T[] toArray(T[] a) { return s.toArray(a); }
@Override public boolean equals(Object o) { return s.equals(o); }
@Override public int hashCode() { return s.hashCode(); }
@Override public String toString() { return s.toString(); }
}
//继承时由于不了解超类的实现细节导致的错误
@SuppressWarnings("all")
class InstrumentedHashSet<E> extends HashSet<E> {
private int addCount = 0;
public int getAddCount() {
return this.addCount;
}
@Override
public boolean add(E e){
addCount++;
return super.add(e);
}
@Override
public boolean addAll(Collection<? extends E> c){
addCount += c.size();
return super.addAll(c);
}
}
输出为:
3
true
implements by inheritance......
6
true
* 使类和成员的可访问性最小化:
区别设计良好的模块和设计不好的模块,最重要因素在于,这个模块相对于外部的其他模块而言,
是否隐藏其内部数据和其他实现细节。
设计良好的模块会隐藏所有的实现细节,把API和它的实现清晰地隔离开来。模块之间只通过API进行
通信,不需要知道其他模块的内部工作情况(封装)。
封装可以有效地解除组成系统的各模块之间的耦合,使得这些模块可以独立地进行开发、测试、优化、修改。
在public类中使用public访问方法而不是public field:
如果public类在它所在的包外部进行访问,就提供访问方法,以保留将来改变该类的内部表示法的灵活性。
* **********************************************************************
* 使类可变性最小化:
为了使类成为不可变,必须遵循以下规则:
1.不要提供任何会修改对象状态的方法。
2.保证类不会被扩张。一般做法是使这个类成为final。
3.使所有的field数据域都成为final。
4.使所有的field数据域都成为private。
5.确保对于任何可变组件的互斥访问。如果类具有指向可变对象的field域,则必须确保该类的客户端无法获得
指向这些对象的引用。并且永远不使用客户端提供的对象引用初始化field域。
不可变对象本质上是线程安全的,它们不要求同步。当多个线程并发访问这样的对象时,它们不会遭到破坏。
不可变对象可以被自由地共享。
坚决不要为每个getter编写一个相应的setter,除非有很好的理由要让类成为可变类,否则就应该是不可变的。
构造器应该创建完全初始化的对象,并建立起所有的约束关系,不要在构造器或者静态工厂之外再提供public
的初始化方法,同样也不应该提供重新初始化方法。
* **********************************************************************
* 复合优先于继承:
继承是实现代码重用的有力手段,但它并非总是完成这项工作的最佳工具。
在包内部实现继承是非常安全的,在那里子类和超类的实现都处在同一个程序员的控制下。
对于专门为了继承而设计,并具有良好的说明文档的类来说,使用继承也是非常安全的。
然而对普通的具体类进行跨越包边界的继承,则是非常危险的。
继承打破了封装性,子类依赖于其超类中特定功能的实现细节。超类可能随着发行版本的不同而有所变化。
因此子类可能遭到破坏,即使它的代码完全没有改变。
* 复合替代继承:不扩展现有的类,而是在新类中增加一个私有域,引用现有类的一个实例。
特别是那些实现了标准接口的类。新类中的每个方法都可以调用被包含的现有类实例中对应的方法,
并返回它的结构。这种方式被称为转发(forwarding)。
这样得到的类将会非常稳固,它不依赖于现有类的实现细节。即使现有类添加了新的方法,也不会影响新的类。
复合类被称为包装类,这正是Decorator模式的应用。
* *********************************************************************
* 继承的功能非常强大,但它违背了封装原则,只有当子类和超类之间确实存在子类型关系时,才适合使用继承。
要么为继承而设计,并提供说明文档,要么禁止继承:
该类的文档必须精确地描述覆盖每个方法所带来的影响。
为了允许继承,类必须遵循一些约定:构造器决不能调用可被覆盖的方法,无论是直接调用还是间接调用。
超类的构造器在子类之前运行,所以子类中覆盖版本的方法将会在子类的构造器之前就先被调用。
如果该覆盖版本的方法依赖于子类构造器的任何初始化操作,该方法将不会如预期般运行。
* 禁止继承的方法:把类声明为final的。另一种办法是把所有构造器都设置为private,并增加public static的
静态工厂方法替代构造器。
* 覆盖:子类方法的返回值和抛出异常必须小于等于父类方法的返回类型和声明异常,访问限制符必须大于
等于父类方法的访问限制符。