对类的功能的扩展,要多用组合,少用继承。
对于类的扩展,在面向对象的编程过程中,我们首先想到的是类的继承,由子类继承父类,从而完成了对子类功能的扩展。但是,面向对象的原则告诉我们,对类的功能的扩展要多用组合,而少用继承。其中的原因有以下几点:
第一、子类对父类的继承是全部的公有和受保护的继承,这使得子类可能继承了对子类无用甚至有害的父类的方法。换句话说,子类只希望继承父类的一部分方法,怎么办?
第二、实际的对象千变万化,如果每一类的对象都有他们自己的类,尽管这些类都继承了他们的父类,但有些时候还是会造成类的无限膨胀。
第三、 继承的子类,实际上需要编译期确定下来,这满足不了需要在运行内才能确定对象的情况。而组合却可以比继承灵活得多,可以在运行期才决定某个对象。
嗨!光说这么多一二三有什么用,我们就是想看看实际情况是不是像上面说的那样呢?还是来看看实际的例子吧!
现在我们需要这样一个HashMap,它除了能按常规的Map那样取值,如get(Object obj)。还能按位取值,像ArrayList那样,按存入对象对的先后顺序取值。
对于这样一个问题,我们首先想到的是做一个类,它继承了HashMap类,然后用一个ArrayList属性来保存存入的key,我们按key的位来取值,代码如下:
Java代码

public class ListMap extends HashMap {
private List list;
public ListMap() {  super();  this.list = new ArrayList();
}
public Object put(Object key,Object value)
{  if(list.contains(key))  {  list.remove(key);  }  this.list.add(key);  return super.put(key,value);
}
public Object getKey(int i)
{  return this.list.get(i);
}
public Object getValue(int i)
{  return this.get(getKey(i));
}
public int size()
{  return this.list.size();
}
}

这个ListMap类对HashMap作了一定的扩展,很简单就实现了上面我们所要求的功能。然后我们对该类做一下测试:

ListMap map = new ListMap();  map.put("a","111");  map.put("v","190");  map.put("d","132");  for(int i=0;i<map.size();i++)  {  System.out.println(map.getValue(i));  }

测试结果为:
111
190
132
正是我们所需要看到的结果。如此说来,这个ListMap类就可以放心的使用了吗?有实现了这样功能的类,你的同事或朋友也可能把这个类拿来使用一下,他可能写出来如下的代码:
Java代码

ListMap map = new ListMap();  map.put("a","111");  map.put("v","190");  map.put("d","132");  String[] list = (String[])map.values().toArray(new String[0]);  for(int i=0;i<list.length;i++)  {  System.out.println(list[i]);  }

运行的结果如下:
132
111
190
哎哟,怎么回事啊?与上面的顺序不对了。你朋友过来找你,说你写的代码怎么不对啊?你很吃惊,说把代码给我看看。于是你看到了上面的代码。你大骂道,混蛋,怎么不是用我的getValue方法啊?你朋友搔搔头道,values方法不是一样的吗?你也没告诉我不能用啊?
通过上面的例子,我们看到了继承的第一个危害:继承不分青红皂白的把父类的公有和受保护的方法统统继承下来。如果你的子类没有对一些方法重写,就 会对你的子类产生危害。上面的ListMap类,你没有重写继承自HashMap类的values方法,而该方法仍然是按HashMap的方式取值,没有 先后顺序。这时候,如果在ListMap类的对象里使用该方法取得的值,就没有实现我们上面的要求。
接上面的那个例子,你听了朋友的抱怨,摇摇头,想想也是,不能怪他。你只得把values方法在ListMap类重写一遍,然后又嘀咕着,我是不是该把HashMap类的公有方法在ListMap类里全部重写?很多方法根本没有必要用到啊?……
对了,很多方法在ListMap里根本不必用到,但是你用继承的话,还不得不在ListMap里重写它们。如果用组合的话,就没有上面的烦恼了:
Java代码 收藏代码

public class MyListMap {
private HashMap map;
private List list;
public MyListMap()
{  this.map = new HashMap();  this.list = new ArrayList();
}
public Object put(Object key,Object value)
{  if(list.contains(key))  {  list.remove(key);  }  this.list.add(key);  return this.map.put(key,value);
}
public Object getKey(int i)
{  return this.list.get(i);
}
public Object getValue(int i)
{  return this.map.get(getKey(i));
}
public int size()
{  return this.list.size();
}
}

这样,你的朋友就只能使用你的getKey和getValue方法了。如果他向你抱怨没有values方法,你尽可以满足他的要求,给他添加上那个方法,而不必担心可能还有方法没有被重写了。
我们来看Adapter模式,该模式的目的十分简单:我手里握有一些实现了WhatIHave接口的实现,可我觉得这些实现的功能不够用,我还需要从Resource类里取一些功能来为我所用。Adapter模式的解决方法如下:
Java代码 收藏代码

public interface WhatIHave
{  public void g();
}
public class Resource
{  public void f()  {  ……  }  public void h()  {  ……  }
}

上面是两个基础类,很明显,我们所要的类既要有g()方法,也要有f()和h()方法。
Java代码 收藏代码

Public class WhatIWant implements WhatIHave
{  private Resource res;  public WhatIWant()  {  res = new Resource();
}
public void g()
{  ……
}
public void f()
{  this.res.f();
}
public void h()
{  this.res.h();
}
}

上 面就是一个Adapter模式最简单的解决问题的思路。我们主要到,对于Resource类,该模式使用的是组合,而不是继承。这样使用是有多个原因:第 一,Java不支持多重继承,如果需要使用好几个不同的Resource类,则继承解决不了问题。第二,如果Resource类还有一个方法:k(),我 们在WhatIWant类里使用不上的话,继承就给我们造成多余方法的问题了。
如果说Adapter模式对组合的应用的目的十分简单明确,那么Decorator模式对组合的应用简直就是令人叫绝。
让我们还是从Decorator模式的最佳例子说起,咖啡店需要售卖各种各样的咖啡:黑咖啡、加糖、加冰、加奶、加巧克力等等。顾客要买咖啡,他可以往咖啡任意的一种或几种产品。
这个问题一提出来,我们最容易想到的是继承。比如说加糖咖啡是一种咖啡,满足ia a的句式,很明显,加糖咖啡是咖啡的一个子类。于是,我们马上可以赋之行动。对于咖啡我们做一个咖啡类:Coffee,咖啡加 糖:SugarCoffee,咖啡加冰:IceCoffee,咖啡加奶:MilkCoffee,咖啡加巧克力:ChocolateCoffee,咖啡加糖 加冰:SugarIceCoffee……
哎哟,我们发现问题了:这样下去我们的类好多啊。可是咖啡店的老板还不放过我们,他又逼着我们增加蒸汽咖啡、加压咖啡,结果我们发现,每增加一种新的类型,我们的类好像是成几何级数增加,我们都要疯了。
这个例子向我们展示了继承的第二个缺点,会使得我们的子类快速的膨胀下去,达到惊人的数量。
怎么办?我们的Decorator模式找到了组合来为我们解决问题。下面我们来看看Decorator模式是怎么来解决这个问题的。
首先是它们的共同接口:
Java代码 收藏代码

package decorator;  interface Product {
public double money();
}  //咖啡类:
class Coffee implements Product {
public double money() {  return 12;
}
}  //加糖:
class Sugar implements Product {
private Product product;  public Sugar(Product product) {  this.product = product;
}  public double money() {  return product.money() + 2;
}
}  //加冰:
class Ice implements Product {
private Product product;  public Ice(Product product) {  this.product = product;
}  public double money() {  return product.money() + 1.5;
}
}  //加奶:
class Milk implements Product {
private Product product;  public Milk(Product product) {  this.product = product;
}  public double money() {  return product.money() + 4.0;
}
}  //加巧克力:
class Chocolate implements Product {
private Product product;  public Chocolate(Product product) {  this.product = product;
}  public double money() {  return product.money() + 5.5;
}
}
public class DecoratorModel{
public static void main(String [] args){  Product coffee = new Coffee();  Product sugarCoffee = new Sugar(coffee);  Product sugarmilkCoffee = new Milk(sugarCoffee);  System.out.println("加糖咖啡:"+sugarCoffee.money());  System.out.println("加糖加奶咖啡:"+sugarmilkCoffee.money());
}
}

我们来看客户端的调用。
如果顾客想要黑咖啡,调用如下:
Product prod = new Coffee();
System.out.println(prod.money());
如果顾客需要加冰咖啡,调用如下:
Product prod = new Ice(new Coffee());
System.out.println(prod.money());
如果顾客想要加糖加冰加奶加巧克力咖啡,调用如下:
Product prod = new Chocolate(new Milk(new Ice(new Sugar())));
System.out.println(prod.money());
通过上面的例子,我们可以看到组合的又一个很优越的好处:能够在运行期创建新的对象。如上面我们的加冰咖啡,我们没有这个类,却能通过组合在运行期创建该对象,这的确大大的增加了我们程序的灵活性。
如果咖啡店的老板再要求你增加加压咖啡,你就不会再担心了,只给他增加了一个类就解决了所有的问题。

为什么说多用组合,少用继承?相关推荐

  1. 为何说要多用组合少用继承?

    在面向对象编程中,有一条非常经典的设计原则,那就是:组合优于继承,多用组合少用继承.为什么不推荐使用继承?组合相比继承有哪些优势?如何判断该用组合还是继承?今天,我们就围绕着这三个问题,来详细讲解一下 ...

  2. 理论七:为何说要多用组合少用继承?如何决定该用组合还是继承?

    在面向对象编程中,有一条非常经典的设计原则,那就是:组合优于继承,多用组合少用继承.为什么不推荐使用继承?组合相比继承有哪些优势?如何判断该用组合还是继承?今天,我们就围绕着这三个问题,来详细讲解一下 ...

  3. 【Java设计模式 面向对象设计思想】五 多用组合少用继承编程

    我们经常会听到说多用组合少用继承,但是拜托,继承是面向对象四大特性之一啊,为什么地位反不如组合了呢,为什么不推荐使用继承?组合相比继承有哪些优势?如何判断该用组合还是继承?围绕这三个问题进行以下讨论 ...

  4. 为什么要多用组合少用继承?

    面向对象编程时,有十条很重要的原则: 代码复用 封装变化 开闭原则 单一职责原则 依赖注入/依赖倒置原则 里氏替换原则(LSP) 接口隔离原则(ISP) 多用组合,少用继承 面向接口编程 委托原则 上 ...

  5. 在设计原则中,为什么反复强调组合要优于继承?

    今日推荐21 款 yyds 的 IDEA插件这 56 个代码注释让我笑吐了注解+反射优雅的实现Excel导入导出(通用版)Fluent Mybatis 牛逼!Nginx 常用配置清单这玩意比Threa ...

  6. 面向对象设计原则——优先使用对象组合,而不是继承(组合以及与继承的区别)

    看到面向对象设计原则中的合成复用原则: 优先使用对象组合,而不是继承 类继承:也叫白箱复用 对象组合:也叫黑箱复用. 继承某种程度上破坏了封装性,子父类之间的耦合性过高. 对象组合只要求被组合的对象具 ...

  7. OC 组合实现多继承

    OC无法完全先C++使用多继承,但可以采用组合的模式来代替继承模式.(协议实现)实现多继承的代码:举例现在ClassC需要继承ClassA中methodA.ClassB中methodB,具体的代码为: ...

  8. C++编程进阶6(public继承与组合、private继承、多重继承、处理模板基类内的名称、如何避免模板代码膨胀)

    二十一.public继承与组合 public继承是是子类对象is a基类对象的关系,比如QT中的所有组件类都要继承QObject,所以所有的QT组件都是一个QObject. 而组合是has a(包含) ...

  9. python 类继承和组合_python3--类与继承和组合

    类和继承:"是一个"关系 我们已经深入探索了继承的机制,这里举个例子来说明它是如何用于模拟真实世界的关系的.从程序员的角度来看,继承是由属性点号运算启动的,由此触发实例.类以及任何 ...

  10. [UE4] Component BluePrint 组合 代替 BluePrint 继承 实现 ECS 结构

    ECS 使用组件的组合代替 Actor 的继承,就是出于简化系统复杂度的考虑 我在网上看到的示例是说,系统要遍历他使用到的组件,我想的是,其实也可以是,要交互的时候就访问实体是否有这个组件,有的话这个 ...

最新文章

  1. FreeSWITCH中文语音包
  2. 2020最后一个月,近4成应届生未就业,19个头部城市谁最留不住人?
  3. python加颜色_python如何给指定的词语加上颜色,并写入Excel文档?
  4. 反思项目调试整体过程
  5. ASP.NET Core 指定环境发布(hosting environment)
  6. [Leedcode][JAVA]第[945]题
  7. 4-1:shell编程之编写第一个shell脚本
  8. 孪生神经网络_基于局部和全局孪生网络的鲁棒的人脸跟踪
  9. 前端埋点的缺点_【埋点学习埋点质量】埋点的框架设计及其准确性
  10. Mac电脑上怎么添加密码提示?操作教程来啦!
  11. Transfrom在64bit服务下面无法运行
  12. mysql-connector-java-5.1.22下载及安装
  13. 使用Tortoise小乌龟 git 拉取代码 报错128处理
  14. 适用于protel99SE初学者
  15. 使用二进制编辑器制作操作系统启动盘
  16. 【Excel文件合并工具】
  17. 3次根号计算机在线应用,根号计算器,三次根号计算器
  18. QT如何实现二级下拉菜单(Combo box)
  19. 解决用友固定资产出现的“不能月末结账,可能月末未结账”的处理办法
  20. 5分钟之内,让你轻松掌握面试流程

热门文章

  1. 怎么删除网络文件服务器的帐号,linux连个文件都删除不了,什么鬼!
  2. WIN10系统连接蓝牙音箱,显示已连接但无声音
  3. 两个微信号绑定一个服务器ip,一个手机号能绑定几个微信账号(一个手机号注册多个微信号的方法)...
  4. 基于银河麒麟 V10 系统安装和卸载 DM8 数据库
  5. 测试两台计算机网络情况,使用iperf测试两台电脑之间的网速
  6. 怎样才能画好古代汉服?画好古代汉服有哪些技巧?
  7. iphone设置邮箱设置_如何使用iPhone设置Chromecast
  8. asp.net实现动态显示当前日期时间
  9. 神调侃!程序员专属成长书单,我比女朋友更了解你!
  10. 2019~2020数字货币领域发展趋势报告