优先使用对象组合,而不是类继承

    1. 类继承通常为“白箱复用”,对象组合通常为“黑箱复用”

    原因

    1. 继承在某种程度上破坏了封装性,子类父类耦合度过高
    2. 而对象组合只要求被组合的对象具有良好定义的外部接口,耦合度相对较低
    3. 子类不需要这么多的方法(继承其实就是复制,会导致方法冗余),并且父类的方法可能有害
      1. 子类对父类的继承是全部的公有和受保护的继承,这使得子类可能继承了对子类无用、甚至有害的父类方法。
      2. 换句话说,子类只希望继承父类的一部分方法,怎么办?
    4. 多重继承会导致类之间的关系非常庞杂
      1. 实际的对象千变万化,如果每一类的对象都有他们自己的类,尽管这些类都继承了他们的父类,但有些时候还是会造成类的无限膨胀
    5. 在类关系的确定时期中,组合的灵活性大
      1. 继承的子类,实际上需要编译期确定下来,这满足不了需要在运行内才能确定对象的情况
      2. 而组合却可以比继承灵活很多,可以在运行期才决定某个对象

    背景:

    1. 类继承在面向对象初期,大概是在80年代末90年代初的时候,非常受人推崇。包括面向对象的初学者也很喜欢用类继承,觉得继承才是面向对象的灵魂核心。
    2. 这可能是来自于父父子子,和我们人类的繁衍关系很像,所以对继承有一种来自于这种层面的,莫名其毛的好感。
    3. 但是这个好感是非常错误的。其实继承关系更像是一种隶属关系,如动物和生物之间、人类和动物之间、汽车和交通工具之间,这才是比较准确的继承关系。并不是像父子之间的那种关系。所以很多类型都不具有继承的关系,因此经常会出现误用继承的情况。

    【例子】父类的方法给子类带来的危害,使得子类达不到设计目的,甚至很难找出bug
    现在我们需要这样一个HashMap,它除了能按常规Map那样取值,如get(Object obj)。还能按位取值,像ArrayList那样,按存入对象的先后顺序取值。

    1. public class ListMap extends HashMap{
    2. private List list;
    3. public ListMap {
    4. super();
    5. this.list = new ArrayList();
    6. }
    7. public Object put(Object key, Object value) {
    8. if(list.contains(key)) list.remove(key);
    9. this.list.add(key);
    10. return super.put(key, value);
    11. }
    12. public Object getKey(int i) {
    13. return this.list.get(i);
    14. }
    15. public Object getValue(int i) {
    16. return this.get(getKey(i));
    17. }
    18. public int size() {
    19. return this.list.size();
    20. }
    21. }
    22. //使用
    23. ListMap map = new ListMap();
    24. map.put("a", "111");
    25. map.put("v", "190");
    26. map.put("d", "132");
    27. String[] list = (String[])map.values().toArray(new String[0]);
    28. for(int i=0; i<list.length; ++i)
    29. System.out.println(list[i]);
    30. //输出:和放入的顺序的不一样,输出的应该是111、190、132
    31. 132
    32. 111
    33. 190
    34. //问题在于,没有使用map.getValue来得到结果
    35. //而使用的是ListMap父类HashMap中的values函数所取得的
    36. //如此产生了问题,而这种问题也是很难被察觉的

    组合方式可以避免这个问题

    1. public class MyListMap{
    2. private HashMap map;
    3. private List list;
    4. public MyListMap() {
    5. this.map = new HashMap();
    6. this.list = new ArrayList();
    7. }
    8. public Object put(Object key, Object value) {
    9. if (list.contains(key)) list.remove(key);
    10. this.list.add(key);
    11. return this.map.put(key.value);
    12. }
    13. public Object getKey(int i) {
    14. return this.list.get(i);
    15. }
    16. public Object getValue(int i) {
    17. return this.map.get( getKey(i) );
    18. }
    19. public int size() {
    20. return this.list.size();
    21. }
    22. }