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

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

组合:继承:

一、基础知识

我们先用代码帮大家来理解一下组合和继承:

1、对于已经存在Parent类时需要扩展其方法时

结构图:

继承代码:

代码

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()
        { 
        }
    }

2、如果发现两个类具有很多代码相同的类需要抽象时,如下图A,B两个类,这两个类中method1,和method3两个方法代码相同

继承:

实现代码:

代码

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() { }
    }

二、继承与组合的优缺点

合 关 系

继 承 关 系

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

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

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

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

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

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

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

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

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

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

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

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

1、为什么继承破坏封装性:

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

2、为什么继承紧耦合:

当作为父类的BaseTable中感觉Insert这个名字不合适时,如果希望将其修改成Create方法,那使用了子类对象Insert方法将会编译出错,可能你会觉得这改起来还算容易,因为有重构工具一下子就好了并且编译错误改起来很容易。但如果BaseTable和子类在不同的程序集中,维护的人员不同,BaseTable程序集升级,那本来能用的代码忽然不能用了,这还是很难让人接受的

3、为什么继承扩展起来比较复杂

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

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

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

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

5、为什么继承 子类不能改变父类接口

如2中的图

子类中觉得Insert方法不合适,希望使用Create方法,因为继承的原因无法改变

三、如何使用继承

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

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

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

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

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

四、组合的缺点:

1、整体类不能自动获得和局部类同样的接口

如果父类的方法子类中几乎都要暴露出去,这时可能会觉得使用组合很不方便,使用继承似乎更简单方便。但从另一个角度讲,实际上也许子类中并不需要暴露这些方法,客户端组合应用就可以了。所以上边推荐不要继承那些不是为了继承而设计的类,一般为了继承而设计的类都是抽象类。

2、创建整体类的对象时,需要创建所有局部类的对象

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

五、相关原则

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

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

白马、黑马
 

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

下边那个长方形正方形的例子我就不转了,大家可以到上边那个博客地址中了解。

2、合成/聚合复用原则(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的过程中得到了大量的强调,但并不意味着应该尽可能地到处使用它。相反,使用它时要特别慎重。只有在清楚知道继承在所有方法中最有效的前提下,才可考虑它。 继承最大的优点就是扩展简单,但大多数缺点都很致命,但是因为这个扩展简单的优点太明显了,很多人并不深入思考,所以造成了太多问题,希望这篇文章能引发你一些思考。

转载于:https://www.cnblogs.com/keyyang/p/3941548.html

OOP组合和继续的优缺点相关推荐

  1. 组合、聚合、继承详解

    有人学了继承,认为他是面向对象特点之一,就在所有能用到继承的地方使用继承,而不考虑究竟该不该使用,无疑,这是错误的.那么,究竟该如何使用继承呢? java中类与类之间的关系 大部分的初学者只知道jav ...

  2. java依赖_java 依赖、组合、聚合与继承

    java中类与类之间的关系 大部分的初学者只知道java中两个类之间可以是继承与被继承的关系,可是事实上,类之间的关系大体上存在五种-继承(实现).依赖.关联.聚合.组合. 接下来,简单的分析一下这些 ...

  3. [Android设计模式之旅]——设计模式的6大原则

    前言 已经很久没有写博客了,今天开始继续学习Android的23种设计模式,首先在学习设计模式之前我们得先了解设计模式的六大基本原则--单一SRP(单一指责原则).OCP(开闭原则).LSP(里氏替换 ...

  4. 面向对象程序设计(OOP设计模式)-结构型模式之装饰器模式的应用与实现

    课程名称:程序设计方法学 实验4:OOP设计模式-结构型模式的应用与实现 时间:2015年11月18日星期三,第3.4节 地点:理1#208 一.实验目的 加深对结构型设计模式的理解以及在开发中的实际 ...

  5. 我所理解的OOP——UML六种关系

    最近由于经常给公司的小伙伴儿们讲一些OOP的基本东西,每次草纸都被我弄的很尴尬,画来画去自己都乱了,有时候也会尝试使用UML表示类之间的关系,但UML从毕业后就再也没接触过了,经常会被小伙伴儿们指出继 ...

  6. 电脑编程python和c加加语言的区别_小猿圈之Bash 和 Python 编程语言优缺点

    Bash 和 Python 是大多数自动化工程师最喜欢的编程语言.它们各有各的优缺点,有时候我们很难抉择,所以,最好的选择它们取决于任务.范围任务的复杂性,跟着小猿圈老师一起来了解一下吧. 让我们来比 ...

  7. thinkphp框架的优缺点

    ThinkPHP的优缺点如下: 1.高级模型:可以轻松支持序列化字段.文本字段.只读字段.延迟写入.乐观锁.数据分表等高级特性. 2.视图模型:轻松动态地创建数据库视图,多表查询相对简单. 3.关联模 ...

  8. 面向对象程序设计(OOP设计模式)-行为型模式之观察者模式的应用与实现

    课程名称:程序设计方法学 实验5:OOP设计模式-行为型模式的应用与实现 时间:2015年12月02日三,第3.4节地点:理 一.实验目的 加深对行为型设计模式的理解以及在开发中的实际应用能力. 二. ...

  9. JavaScript 前端简单工厂模式、工厂方法模式、抽象工厂模式优缺点及详解 — 设计模式《二》

    工厂模式 前言:就前端 JavaScript 语言来说,可以把简单工厂模式.工厂方法模式.抽象工厂模式归纳为一种即工厂模式: 一. 简单工厂模式 介绍/概述: 简单工厂模式的工厂类一般是使用静态方法, ...

最新文章

  1. 16个Linux服务器监控命令
  2. jquery 替换括号里面内容_EXCEL单元格里提取、删除或替换部分内容
  3. My Toolkit of Node.js
  4. linux服务器文件索引inodes满了
  5. code point,code unit
  6. uva 10285——Longest Run on a Snowboard
  7. Ubuntu 12.04 修改/etc/resolv.conf重启后还原成修改前状态解决办法
  8. 《大数据》2015年第2期“研究”——大数据时代的数据传输网
  9. android 播放音乐卡顿,Android MediaPlayer+SeekBar播放音频出现卡顿边长可能问题
  10. 了解HTML CSS格式化排版 文字排版
  11. Oracle DUL/AUL/ODU 工具说明
  12. php防止视频资源被下载
  13. xmapp启动数据库问题记录 Access denied for user ‘me‘@‘localhost‘ (using password: NO)
  14. C/C++编程学习 - 第20周 ⑩ 量身高
  15. 微信任务(投票)分发平台
  16. 又又又一边缘计算初创企业融资
  17. 通讯录搜索排序 类似微信通讯录
  18. 邰正宵 娃娃-小小的梦想(音乐河87)
  19. java根据前序和中序建树_Java实现根据前序遍历构建二叉树(前序遍历、中序遍历、后序遍历)...
  20. 给数子加千分号的方法(每三个数字后加一个逗号)

热门文章

  1. burg算法的matlab代码实现_导向滤波算法及其matlab代码实现
  2. html如何左侧固定div,详解左右宽度固定中间自适应html布局解决方案
  3. mysql 5.7变化_从MySQL 5.5到5.7看复制的演进
  4. 试用c51语言采样连续5次异常_学会这些自闭症儿童语言训练技巧,孩子开口说话不再困难...
  5. java创建activity视图_java-动态创建的视图id始终为null-findviewbyid不起作用
  6. idea 线程内存_Java线程池系列之-Java线程池底层源码分析系列(一)
  7. linux本地yum源与软件包管理,【Linux系统中的】本地yum源的搭建与使用yum源进行软件的下载...
  8. java如何设置圆角边框_巧妙实现带圆角的渐变边框
  9. 在共享dll中使用mfc_在SpringBoot中使用Spring Session解决分布式会话共享问题
  10. python期末设计作品_期末作品检查