在阅读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. react-native-echarts在打包时出现的坑

    react-native-echarts目前是RN开发中使用echarts图表最好的插件了,用法与Echarts完全一致,默认提供了三个属性: option (object): The option ...

  2. servlet(6) 链接数据库

    一.servlet链接mysql步骤: 1.注册驱动器:Class.forName("com.mysql.jdbc.Driver"); 加载类并执行下面的静态语句块,将Driver ...

  3. bugku web web3

    flag就在这里快来找找吧http://123.206.87.240:8002/web3/ 点进去这个页面可以看到不断地有提示 flag就在这里 来找找吧 那就找一找哇 在source中可以找到一行注 ...

  4. [2019.04.01]Linux 学习心得(2)-- tar 命令的理解

    这篇文章并不是发布最早的但是阅读量却每天都见长,很想知道各位大大是怎么找到这篇文章的.如果不忙,还请各位大大评论一下我看看,没准我可以为大家改进一下本文,提升一下质量. =============== ...

  5. 一道php笔试题

    原文地址: http://www.walu.cc/php/a-bishiti.md 问题: 请找出下面代码中的问题,修复并优化. <?php //批量注册用户,每次>100个. //注册新 ...

  6. bzoj 2049: [Sdoi2008]Cave 洞穴勘测 (LCT)

    链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2049 题面: 2049: [Sdoi2008]Cave 洞穴勘测 Time Limit: 1 ...

  7. 使用Portainer管理Docker

    Portainer Portainer安装非常简单,只需不到一分钟.Portainer完全支持Docker 1.10及更高版本. Mac 快速安装运行: 命令行输入: docker pull port ...

  8. win10更改无线网卡的MAC地址

    https://blog.csdn.net/qq_31778495/article/details/80932472 前段时间电脑蹭网被禁了MAC地址,故寻找修改MAC地址的方法. 本机配置: win ...

  9. CSS上下左右居中的几种方法

    1.absolute,margin: auto .container { position: relative; } .content { position: absolute; margin: au ...

  10. html的分类与特点

    ##块级元素##|元素|描述||--|--||h1-h6|标题||div|区分大模块||p|段落||ul>li|无序列表||ol>li|有序列表||dl>dt dd|自定义列表||t ...