组合与继承 区别

在本书中,把UML中的关联关系和聚集关系统称为组合关系。组合与继承都是提高代码可重用性的手段。在设计对象模型时,可以按照语义来识别类之间的组合关系和继承关系。在有些情况下,采用组合关系或者继承关系能完成同样的任务,组合和继承存在着对应关系:组合中的整体类和继承中的子类对应,组合中的局部类和继承中的父类对应,参见表6-1。本章6.9节(小结)中的表6-2总结了组合与继承的优缺点。

 

表6-1 组合与继承的对应关系

组 合 关 系

继 承 关 系

局部类

父类

整体类

子类

从整体类到局部类的分解过程

从子类到父类的抽象过程

从局部类到整体类的组合过程

从父类到子类的扩展过程

值得注意的是,本章所说的整体类和局部类比UML的聚集关系中的整体类和局部类具有更广泛的含义。在本章中,如果在类A中包含类C类型的属性,那么就把类A称为整体类或者包装类,把类C称为局部类或者被包装类。

组合关系的分解过程对应继承关系的抽象过程


图6-11 具有相同行为的类A和类B

      下面的例子未涉及具体的业务领域,该例子分别用组合关系与继承关系来建立一个对象模型。如图6-11所示,类A和类B有相同方法method1()、method2()和method3(),此外类A和类B还分别拥有methodA()和methodB()方法。在method3()方法中访问method1()方法,在mehodA()和methodB()方法中都会访问method2()方法。以下是类A和类B的源程序。
 

  1. public class A{  
  2. private void method1(){System.out.println("method1");}  
  3. private void method2(){System.out.println("method2");}  
  4. public void method3(){method1();System.out.println("method3");}  
  5. public void methodA(){method2();System.out.println("methodA");}  
  6. }  
  7. public class B{  
  8. private void method1(){System.out.println("method1");}  
  9. private void method2(){System.out.println("method2");}  
  10. public void method3(){method1();System.out.println("method3");}  
  11. public void methodB(){method2();System.out.println("methodB");}  
  12. }  
 

图6-12 从类A和类B中抽象出父类C

1.使用继承关系

      在图6-12中,从类A和类B中抽象出父类C,它包含method1()、method2()和method3()方法。由于在类A和类B中都会访问method2()方法,因此把method2()方法声明为protected类型。

  1. public class C{  
  2. private void method1(){System.out.println("method1");}  
  3. protected void method2(){System.out.println("method2");}  
  4. public void method3(){method1();System.out.println("method3");}  
  5. }  
  6. public class A extends C{  
  7. public void methodA(){method2(); System.out.println("methodA");}  
  8. }  
  9. public class B extends C{  
  10. public void methodB(){method2(); System.out.println("methodB");}  
  11. }  

2.使用组合关系

      在图6-13中,类A与类C,以及类B与类C之间为组合关系。在类A中定义了C类型的引用变量c,类A的method3()方法直接调用类C的method3()方法。类A对类C进行了封装,类A被称为包装类,同样,类B也是包装类。由于在类A和类B中都会访问private类型的method2()方法,因此不能把method2()方法放在类C中定义,因为如果这样做,就必须在类C中把method2()方法定义为public类型,而这彻底破坏了封装。以下是类C、类A和类B的源程序。

  1. public class C{  
  2. private void method1(){System.out.println("method1");}  
  3. public void method3(){method1();System.out.println("method3");}  
  4. }  
  5. public class A {  
  6. private C c;  
  7. public A(C c){this.c=c;}  
  8. private void method2(){System.out.println("method2");}  
  9. public void method3(){c.method3();}  
  10. public void methodA(){method2(); System.out.println("methodA");}  
  11. }  
  12. public class B {  
  13. private C c;  
  14. public B(C c){this.c=c;}  
  15. private void method2(){System.out.println("method2");}  
  16. public void method3(){c.method3();}  
  17. public void methodB(){method2(); System.out.println("methodB");}  
  18. }  

图6-13 从类A与类B中分解出局部类C

 

      组合关系和继承关系相比,前者的最主要优势是不会破坏封装,当类A与类C之间为组合关系时,类C封装实现,仅向类A提供接口;而当类A与类C之间为继承关系时,类C会向类A暴露部分实现细节。在软件开发阶段,组合关系虽然不会比继承关系减少编码量,但是到了软件维护阶段,由于组合关系使系统具有较好的松耦合性,因此使得系统更加容易维护。

组合关系的缺点是比继承关系要创建更多的对象。以下程序演示在两种关系下创建类A的实例并且调用其methodA()方法。

  1. //类A与类C为组合关系  
  2. C c=new C();  
  3. A a=new A(c);  
  4. a.methodA();  
  5. // 类A与类C为继承关系  
  6. A a=new A();  
  7. a.methodA();  

      从以上程序看出,对于组合关系,创建整体类的实例时,必须创建其所有局部类的实例;而对于继承关系,创建子类的实例时,无须创建父类的实例。继承关系最大的弱点是打破了封装,子类能够访问父类的实现细节,子类与父类之间紧密耦合,子类缺乏独立性,从而影响了子类的可维护性。为了尽可能地克服继承的这一缺陷,应该遵循以下原则:

·精心设计专门用于被继承的类,继承树的抽象层应该比较稳定。

·对于父类中不允许覆盖的方法,采用final修饰符来禁止其被子类覆盖。

·对于不是专门用于被继承的类,禁止其被继承。

·优先考虑用组合关系来提高代码的可重用性。

本章对组合关系和继承关系进行了比较,表6-2对这两种关系的优缺点做了总结。

 

表6-2 比较组合关系与继承关系

组 合 关 系

继 承 关 系

优点:不破坏封装,整体类与局部类之间松耦合,彼此相对独立

缺点:破坏封装,子类与父类之间紧密耦合,子类依赖于父类的实现,子类缺乏独立性

优点:具有较好的可扩展性

缺点:支持扩展,但是往往以增加系统结构的复杂度为代价

优点:支持动态组合。在运行时,整体对象可以选择不同类型的局部对象

缺点:不支持动态继承。在运行时,子类无法选择不同的父类

优点:整体类可以对局部类进行包装,封装局部类的接口,提供新的接口

缺点:子类不能改变父类的接口

缺点:整体类不能自动获得和局部类同样的接口

优点:子类能自动继承父类的接口

缺点:创建整体类的对象时,需要创建所有局部类的对象

优点:创建子类的对象时,无须创建父类的对象

原文地址:https://www.cnblogs.com/xflash/p/3771644.html