在阅读Effective Java中的第16条时发现了一个有趣的机制或者说是模式,那就是组合(文中翻译为复用,但是作者认为组合更能体现这种模式的精神),并且文中建议使用组合。
  那什么是组合,组合相较于继承的优点在哪里,缺点又有哪些,组合和继承更适合怎样的场景,更重要的是作者在校基础课程的学习中尽然都没有接触到组合这个概念,实在有理由探索一下!

我们分别了解一下组合和继承的概念

组合

  • 组合就是 A类的对象是B类的成员变量。相当于 A类对象是B类对象的一个变量,A类中的所有功能,B类都可以通过A类对象的调用来实现。
  • 组合体现的是整体与部分、拥有的关系,即 has - a 的关系

让我们用一段代码来演示一下


class 电池{ public void 放电(){
System.out.println("输出电能");
} } class 屏幕{ public void 亮屏{
System.out.println("显示内容");
} } class CPU{ public void 运算{
System.out.println("浮点计算");
} } class 手机{ private 电池 dc;
private 屏幕 pm;
private CPU cpu; 手机(电池 dc,屏幕 pm,CPU cpu){
this.dc = dc;
this.pm = pm;
this.cpu = cpu;
} public 开机(){ dc.放电();
cpu.运算();
pm.亮屏(); System.out.println("开机成功");
} public static void main(String[] args) { 电池 dc = new 电池();
屏幕 pm = new 屏幕();
CPU cpu = new CPU(); 手机 sj = new 手机(dc,pm,cpu);
sj.开机(); } }

继承

  • 一个类继承另外的一个类的功能,并可以增加它自己的新功能的能力
  • 继承体现的是父与子,血缘的关系,即 is - a 的关系

让我们继续放代码来演示


class Animal{ private void beat(){
System.out.println("心脏跳动...");
} public void breath(){
beat();
System.out.println("呼吸中...");
} } class Bird extends Animal{ //创建子类独有的方法fly()
public void fly(){
System.out.println("我是鸟,我在天空中自由的飞翔...");
} } class Wolf extends Animal{ //创建子类独有的方法run()
public void run(){
System.out.println("我是狼,我在草原上快速奔跑...");
} } public class InheritTest{ public static void main(String[] args){ Bird b=new Bird();
b.breath();
b.fly(); Wolf w=new Wolf();
w.beat();
w.run(); ---------- 运行Java程序 ---------- 呼吸中...
我是鸟,我在天空中自由的飞翔... 心脏跳动...
我是狼,我在草原上快速奔跑... }
}

区别和联系

区别

  • 组合是一种 has-a 的 关系
  • 继承是一种 is-a 的 关系
  • 继承结构中,父类的内部细节对于子类是可见的。所以通过继承的代码复用是一种白盒式代码复用。
  • 如果父类的实现跟随版本而发生改变,那么子类的实现也将随之改变。这样就导致了子类行为的不可预知性;

  • 组合是通过对现有的类进行拼装(组合)产生新的、更复杂的功能。因为在类之间,各自的内部细节是不可见的,所以这种方式的代码复用是黑盒式代码复用。(因为组合中一般都定义一个类型,所以在编译期根本不知道具体会调用哪个实现类的方法)

  • 继承,在写代码的时候就要指名具体继承哪个类,所以,在编译期就确定了关系。(从基类继承来的实现是无法在运行期动态改变的,因此降低了应用的灵活性。)
  • 组合,在写代码的时候可以采用面向接口编程。所以,类的组合关系一般在运行期确定。

联系

  • 组合和继承都是代码复用的一种方式

优点和缺点

  在 Effective Java 中非常推荐在需要代码重用的情况下优先使用组合而不是大家常见的继承,让我们们他们各有什么优缺点

  • 优点
组合 继承
不破坏封装,整体类与局部类之间松耦合,彼此相对独立 子类能自动继承父类的接口
具有较好的可扩展性 创建子类的对象时,无须创建父类的对象
支持动态组合。在运行时,整体对象可以选择不同类型的局部对象
整体类可以对局部类进行包装,封装局部类的接口,提供新的接口
  • 缺点
组合 继承
整体类不能自动获得和局部类同样的接口 子类不能改变父类的接口
创建整体类的对象时,需要创建所有局部类的对象 不支持动态继承。在运行时,子类无法选择不同的父类
支持扩展,但是往往以增加系统结构的复杂度为代价
破坏封装,子类与父类之间紧密耦合,子类依赖于父类的实现,子类缺乏独立性

  可以发现组合相对于继承是一种更好的代码重用手段,但是继承就没有用武之地么,让我们接下来看他们各自的使用场合和情况

不同的场景

  • 我们通过最初的组合和继承的示例代码再结合 概念中的 has - a 和 is -a 的描述,能够体会到组合和继承是为了两种场景的重用而设计的
  1. 当B类真正是A类的 抽象类型时,才适用继承,例如 人 是 婴儿的 抽象类型 是适用于继承的 ,而当 猴子 为 了 重用 人 的呼吸,进食,行动 而 继承于 人 这就是 不适用的.
  2. 而除去第一种场景,其他场景使用组合是更合适的
  3. 并且在第一种场景中,使用组合也不是不可以,但继承是最好的方案

结语

  文章至此处,想必大家已经明白了组合和继承的关系,并且已经知道在何种情况下使用他们,那么组合VS继承 的 结果就是在更多的场景中使用组合是更好的方案。

面向对象之组合VS继承:继承过时了?的更多相关文章

  1. Python面向对象(组合、菱形继承、多态)

    今日内容: 1.组合 2.菱形继承 3.多态与多态性 昨天内容重点回顾: 1)调用也叫实例化:发生了2件事  1.创造空对象  2.触发对象下的__init__方法,然后将p连同参数一同传给init  ...

  2. Python面向对象的三大特性之继承和组合

    继承和组合 一.组合 组合:组合指的是,在一个类中以另外一个类的对象(也就是实例)作为数据属性,称为类的组合 也就是说:一个类的属性是另一个类的对象,就是组合 例子: 圆环是由两个圆组成的,圆环的面积 ...

  3. javascript之面向对象程序设计(对象和继承)

    总结的文章略长,慎点. 知识点预热 引用类型:引用类型的值(对象)是引用类型的一个实例.在ECMAScript中,引用类型是一种数据结构,用于将数据和功能组织在一起.在其他面向对象语言中被称为类,虽然 ...

  4. JS--我发现,原来你是这样的JS:面向对象编程OOP[3]--(JS继承)

    一.面向对象编程(继承) 这篇博客是面向对象编程的第三篇,JS继承.继承顾名思义,就是获取父辈的各种"财产"(属性和方法). 怎么实现继承? 我们的JavaScript比较特别了, ...

  5. python基础学习Day17 面向对象的三大特性之继承、类与对象名称空间小试

    一.课前回顾 类:具有相同属性和方法的一类事物 实例化:类名() 过程: 开辟了一块内存空间 执行init方法 封装属性 自动的把self返回给实例化对象的地方 对象:实例 一个实实在在存在的实体 组 ...

  6. 1.Java基础-面向对象编程思想(封装继承多态接口)

    封装: 1.定义:隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别. 2.封装的目的是:增强安全性和简化编程,使用者不必了解具体的实现细节,而只是要通过外部接口,一特定的 ...

  7. Javascript面向对象特性实现封装、继承、接口详细案例——进级高手篇

    Javascript面向对象特性实现(封装.继承.接口) Javascript作为弱类型语言,和Java.php等服务端脚本语言相比,拥有极强的灵活性.对于小型的web需求,在编写javascript ...

  8. JavaScript 面向对象程序设计(下)——继承与多态 【转】

    JavaScript 面向对象程序设计(下)--继承与多态 前面我们讨论了如何在 JavaScript 语言中实现对私有实例成员.公有实例成员.私有静态成员.公有静态成员和静态类的封装.这次我们来讨论 ...

  9. (二)Javascript面向对象编程:构造函数的继承

    Javascript面向对象编程:构造函数的继承   这个系列的第一部分,主要介绍了如何"封装"数据和方法,以及如何从原型对象生成实例. 今天要介绍的是,对象之间的"继承 ...

随机推荐

  1. [转帖]windows10,business版和consumer版本区别

    windows10,business版和consumer版本区别 时间:2018-07-08 10:50来源:原创 作者:5分享 点击: 7113 次 windows10系统(1803)busines ...

  2. Ubuntu16设置Redis开机自启动

    Ubuntu16设置Redis开机自启动   Ubuntu16设置Redis开机自启动 设置条件: -Ubuntu16.04 -Redis-4.0.11 在redis目录下找到  utils/redi ...

  3. Xcode 打包(Shell)

    1. xcodebuild 1.1 查当前可用的 sdk $xcodebuild -showsdks 1.2 查看所支持的target $xcodebuild -target 1.2 清空当前编译的a ...

  4. python测试工程师高端基础面试题整理

    面试总括篇 技术技能 开发语言:python 数据库:mysql 操作系统;linux 网络协议基础 测试技能:自动化(UIselenium+接口)+性能 业务知识 测试工程师执业规划 初级--> ...

  5. DRF 商城项目 - 用户操作(收藏, 留言, 收货地址)

    个人收藏 整体逻辑类似于 个人中心 ( 个人中心的相关逻辑梳理详情  点击这里 ) 也是两个序列化组价的分流 查看收藏  ( list ) 详情指向 收藏详情 的组价 创建收藏 ( create ) ...

  6. found 12 vulnerabilities (7 moderate, 5 high) run `npm audit fix` to fix them, or `npm audit` for details

    npm 安装包之后,如果出现类似下面的信息 found 12 vulnerabilities (7 moderate, 5 high) run `npm audit fix` to fix them, ...

  7. How to expand Azure VM OS Disk

    There are three main disk roles in Azure: the data disk, the OS disk, and the temporary disk. 1. OS ...

  8. ADRC-active disturbance rejection control-自抗扰控制器

    ADRC自抗扰控制基本思想要点: 1.标准型与总扰动,扩张状态与扰动整体辨识,微分信号生成与安排过渡过程以及扰动的消减与控制量产生. ADRC主要构成: 1)跟踪微分器(TD)---the track ...

  9. shiro多Realm第一次调用不生效问题

    1. 由于最近自己写的一个项目上用到了多realm的使用,遇到了一个这样的问题: 1. 自己继承了BasicHttpAuthenticationFilter,实现了获取token,然后直接请求api的 ...

  10. vue中的computed(计算属性)和watch(监听属性)的特点,以及深度监听

    //计算属性是根据data中已有的属性,计算得到一个新的属性, <div>全名:{{fullName}}</div> 创建计算属性通过computed关键字,它是一个对象 计算 ...