OOP组合和继续的优缺点 —— 详解继承与组合的优缺点

—— 详解继承与组合的优缺点

 

组合与继承都是提升代码可重用性的手段。在设计对象模型时,能够按照语义来识别类之间的组合关系和继承关系。在有些状况下,采用组合关系或者继承关系能完成一样的任务,组合和继承存在着对应关系:组合中的总体类和继承中的子类对应,组合中的局部类和继承中的父类对应,以下图:html

组合:继承:ide

1、基础知识工具

咱们先用代码帮你们来理解一下组合和继承:post

一、对于已经存在Parent类时须要扩展其方法时学习

结构图:url

继承代码:spa

 

代码
复制代码
class Parent
    {
        public void Method1() 
        { 
        }
        public void Method2() { }
        public void Method3() { }
    }
 class Child1:Parent
    {
        public void MethodA() { }
    }
 class Child2:Parent
    {
        public void MethodA() { }
    }
复制代码

 

组合代码:设计

 

代码
复制代码
class ComponentA
    {
        Parent p = new Parent();
        public void Method2()
        {
            p.Method2();
        }
        public void MethodA()
        {
        }
    }
 class ComponentB
    {
        Parent p = new Parent();
        public void Method2() {
            p.Method2();
        }
        public void MethodB()
        { 
        }
    }
复制代码

 

二、若是发现两个类具备不少代码相同的类须要抽象时,以下图A,B两个类,这两个类中method1,和method3两个方法代码相同3d

继承:code

实现代码:

 

代码
复制代码
class A:C
    {
        public void MethodB() { }
        public override void Method2()
        {
            
        }
    }
    class B:C
    {
        public override void Method2()
        {
            
        }
        public void MethodB() { }
    }
    class C
    {
        public void Method1() { }
        public virtual void Method2() { }
        public void Method3() { }
    }
复制代码

 

 

组合:

实现代码:

 

代码
复制代码
class A
    {
        public void MethodA() { }
        C c = new C();
        public void Method1()
        {
            c.Method1();
        }
        public void Method2() { }

    }
    class B
    {
        public void MethodB() { }
        public void Method2() { }
    }
    class C
    {
        public void Method1() { }
        public void Method2() { }
        public void Method3() { }
    }
复制代码

 

2、继承与组合的优缺点

 

合 关 系

继 承 关 系

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

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

优势:具备较好的可扩展性

缺点:支持扩展,可是每每以增长系统结构的复杂度为代价

优势:支持动态组合。在运行时,总体对象能够选择不一样类型的局部对象

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

优势:总体类能够对局部类进行包装,封装局部类的接口,提供新的接口

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

缺点:总体类不能自动得到和局部类一样的接口

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

缺点:建立总体类的对象时,须要建立全部局部类的对象

优势:建立子类的对象时,无须建立父类的对象

 

一、为何继承破坏封装性:

 

鸭子中不想要“飞”的方法,但由于继承没法封装这个无用的“飞”方法 。

二、为何继承紧耦合:

 

看成为父类的BaseTable中感受Insert这个名字不合适时,若是但愿将其修改为Create方法,那使用了子类对象Insert方法将会编译出错,可能你会以为这改起来还算容易,由于有重构工具一会儿就行了而且编译错误改起来很容易。但若是BaseTable和子类在不一样的程序集中,维护的人员不一样,BaseTable程序集升级,那原本能用的代码突然不能用了,这仍是很难让人接受的

三、为何继承扩展起来比较复杂

当图书和数码的算税方式和数码产品同样时,而消费类产品的算税方式是另外一样时,若是采用继承方案可能会演变成以下方式:

这样若是产品继续增长,算税方式继续增长,那继承的层次会很是复杂,并且很难控制,而使用组合就能很好的解决这个问题,参见:面向对象思想的头脑风暴(一)

四、继承不能支持动态继承

这个其实很好理解,由于继承是编译期就决定下来的,没法在运行时改变,如3例中,若是用户须要根据当地的状况选择计税方式,使用继承就解决不了,而使用组合结合反射就能很好的解决。

五、为何继承 子类不能改变父类接口

如2中的图

子类中以为Insert方法不合适,但愿使用Create方法,由于继承的缘由没法改变

 

3、如何使用继承

 

一、精心设计专门用于被继承的类,继承树的抽象层应该比较稳定,通常不要多于三层。

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

三、优先考虑用组合关系来提升代码的可重用性。

四、子类是一种特殊的类型,而不仅是父类的一个角色

五、子类扩展,而不是覆盖或者使父类的功能失效

 

 

4、组合的缺点:

一、总体类不能自动得到和局部类一样的接口

若是父类的方法子类中几乎都要暴露出去,这时可能会以为使用组合很不方便,使用继承彷佛更简单方便。但从另外一个角度讲,实际上也许子类中并不须要暴露这些方法,客户端组合应用就能够了。因此上边推荐不要继承那些不是为了继承而设计的类,通常为了继承而设计的类都是抽象类。

二、建立总体类的对象时,须要建立全部局部类的对象

这个可能没什么更好的办法,但在实际应用中并无多出多少代码。

5、相关原则

一、里氏代换原则(LSP)(如下转自http://www.cnblogs.com/zhenyulu/articles/36061.html

Liskov Substitution Principle(里氏代换原则):子类型(subtype)必须可以替换它们的基类型。

 

白马、黑马
 

反过来的代换不成立
《墨子·小取》说:"娣,美人也,爱娣,非爱漂亮人也……"娣即是妹妹,哥哥喜好妹妹,是由于两人是兄妹关系,而不是由于妹妹是个美人。所以,喜好妹妹不等同于喜好美人。用面向对象语言描述,美人是基类,妹妹是美人的子类。哥哥做为一个有"喜好()"方法,接受妹妹做为参数。那么,这个"喜好()"方法通常不能接受美人的实例。

 

 

下边那个长方形正方形的例子我就不转了,你们能够到上边那个博客地址中了解。

二、合成/聚合复用原则(CARP)(如下转自http://www.cnblogs.com/zhenyulu/articles/36068.html
合成/聚合复用原则(Composite/Aggregate Reuse Principle或CARP)常常又叫作合成复用原则(Composite Reuse Principle或CRP),就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新对象经过向这些对象的委派达到复用已有功能的目的。

简而言之,要尽可能使用合成/聚合,尽可能不要使用继承。

o Design to interfaces.
o Favor composition over inheritance.
o Find what varies and encapsulate it.
(摘自:Design Patterns Explained)

区分"Has-A"与"Is-A"

"Is-A"是严格的分类学意义上定义,意思是一个类是另外一个类的"一种"。而"Has-A"则不一样,它表示某一个角色具备某一项责任。

致使错误的使用继承而不是合成/聚合的一个常见的缘由是错误的把"Has-A"看成"Is-A"。

例如:
 

实际上,雇员、经理、学生描述的是一种角色,好比一我的是"经理"必然是"雇员",另一我的多是"学生雇员",在上面的设计中,一我的没法同时拥有多个角色,是"雇员"就不能再是"学生"了,这显然是不合理的。

错误源于把"角色"的等级结构与"人"的等级结构混淆起来,误把"Has-A"看成"Is-A"。解决办法:

 

 

总结:

根据咱们前面讲的内容咱们能够发现继承的缺点远远多于优势,尽管继承在学习OOP的过程当中获得了大量的强调,但并不意味着应该尽量地处处使用它。相反,使用它时要特别慎重。只有在清楚知道继承在全部方法中最有效的前提下,才可考虑它。 继承最大的优势就是扩展简单,但大多数缺点都很致命,可是由于这个扩展简单的优势太明显了,不少人并不深刻思考,因此形成了太多问题,但愿这篇文章能引起你一些思考。

相关文章
相关标签/搜索