为什么说多用组合,少用继承?
对类的功能的扩展,要多用组合,少用继承。
对于类的扩展,在面向对象的编程过程中,我们首先想到的是类的继承,由子类继承父类,从而完成了对子类功能的扩展。但是,面向对象的原则告诉我们,对类的功能的扩展要多用组合,而少用继承。其中的原因有以下几点:
第一、子类对父类的继承是全部的公有和受保护的继承,这使得子类可能继承了对子类无用甚至有害的父类的方法。换句话说,子类只希望继承父类的一部分方法,怎么办?
第二、实际的对象千变万化,如果每一类的对象都有他们自己的类,尽管这些类都继承了他们的父类,但有些时候还是会造成类的无限膨胀。
第三、 继承的子类,实际上需要编译期确定下来,这满足不了需要在运行内才能确定对象的情况。而组合却可以比继承灵活得多,可以在运行期才决定某个对象。
嗨!光说这么多一二三有什么用,我们就是想看看实际情况是不是像上面说的那样呢?还是来看看实际的例子吧!
现在我们需要这样一个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());
通过上面的例子,我们可以看到组合的又一个很优越的好处:能够在运行期创建新的对象。如上面我们的加冰咖啡,我们没有这个类,却能通过组合在运行期创建该对象,这的确大大的增加了我们程序的灵活性。
如果咖啡店的老板再要求你增加加压咖啡,你就不会再担心了,只给他增加了一个类就解决了所有的问题。
为什么说多用组合,少用继承?相关推荐
- 为何说要多用组合少用继承?
在面向对象编程中,有一条非常经典的设计原则,那就是:组合优于继承,多用组合少用继承.为什么不推荐使用继承?组合相比继承有哪些优势?如何判断该用组合还是继承?今天,我们就围绕着这三个问题,来详细讲解一下 ...
- 理论七:为何说要多用组合少用继承?如何决定该用组合还是继承?
在面向对象编程中,有一条非常经典的设计原则,那就是:组合优于继承,多用组合少用继承.为什么不推荐使用继承?组合相比继承有哪些优势?如何判断该用组合还是继承?今天,我们就围绕着这三个问题,来详细讲解一下 ...
- 【Java设计模式 面向对象设计思想】五 多用组合少用继承编程
我们经常会听到说多用组合少用继承,但是拜托,继承是面向对象四大特性之一啊,为什么地位反不如组合了呢,为什么不推荐使用继承?组合相比继承有哪些优势?如何判断该用组合还是继承?围绕这三个问题进行以下讨论 ...
- 为什么要多用组合少用继承?
面向对象编程时,有十条很重要的原则: 代码复用 封装变化 开闭原则 单一职责原则 依赖注入/依赖倒置原则 里氏替换原则(LSP) 接口隔离原则(ISP) 多用组合,少用继承 面向接口编程 委托原则 上 ...
- 在设计原则中,为什么反复强调组合要优于继承?
今日推荐21 款 yyds 的 IDEA插件这 56 个代码注释让我笑吐了注解+反射优雅的实现Excel导入导出(通用版)Fluent Mybatis 牛逼!Nginx 常用配置清单这玩意比Threa ...
- 面向对象设计原则——优先使用对象组合,而不是继承(组合以及与继承的区别)
看到面向对象设计原则中的合成复用原则: 优先使用对象组合,而不是继承 类继承:也叫白箱复用 对象组合:也叫黑箱复用. 继承某种程度上破坏了封装性,子父类之间的耦合性过高. 对象组合只要求被组合的对象具 ...
- OC 组合实现多继承
OC无法完全先C++使用多继承,但可以采用组合的模式来代替继承模式.(协议实现)实现多继承的代码:举例现在ClassC需要继承ClassA中methodA.ClassB中methodB,具体的代码为: ...
- C++编程进阶6(public继承与组合、private继承、多重继承、处理模板基类内的名称、如何避免模板代码膨胀)
二十一.public继承与组合 public继承是是子类对象is a基类对象的关系,比如QT中的所有组件类都要继承QObject,所以所有的QT组件都是一个QObject. 而组合是has a(包含) ...
- python 类继承和组合_python3--类与继承和组合
类和继承:"是一个"关系 我们已经深入探索了继承的机制,这里举个例子来说明它是如何用于模拟真实世界的关系的.从程序员的角度来看,继承是由属性点号运算启动的,由此触发实例.类以及任何 ...
- [UE4] Component BluePrint 组合 代替 BluePrint 继承 实现 ECS 结构
ECS 使用组件的组合代替 Actor 的继承,就是出于简化系统复杂度的考虑 我在网上看到的示例是说,系统要遍历他使用到的组件,我想的是,其实也可以是,要交互的时候就访问实体是否有这个组件,有的话这个 ...
最新文章
- FreeSWITCH中文语音包
- 2020最后一个月,近4成应届生未就业,19个头部城市谁最留不住人?
- python加颜色_python如何给指定的词语加上颜色,并写入Excel文档?
- 反思项目调试整体过程
- ASP.NET Core 指定环境发布(hosting environment)
- [Leedcode][JAVA]第[945]题
- 4-1:shell编程之编写第一个shell脚本
- 孪生神经网络_基于局部和全局孪生网络的鲁棒的人脸跟踪
- 前端埋点的缺点_【埋点学习埋点质量】埋点的框架设计及其准确性
- Mac电脑上怎么添加密码提示?操作教程来啦!
- Transfrom在64bit服务下面无法运行
- mysql-connector-java-5.1.22下载及安装
- 使用Tortoise小乌龟 git 拉取代码 报错128处理
- 适用于protel99SE初学者
- 使用二进制编辑器制作操作系统启动盘
- 【Excel文件合并工具】
- 3次根号计算机在线应用,根号计算器,三次根号计算器
- QT如何实现二级下拉菜单(Combo box)
- 解决用友固定资产出现的“不能月末结账,可能月末未结账”的处理办法
- 5分钟之内,让你轻松掌握面试流程
热门文章
- 怎么删除网络文件服务器的帐号,linux连个文件都删除不了,什么鬼!
- WIN10系统连接蓝牙音箱,显示已连接但无声音
- 两个微信号绑定一个服务器ip,一个手机号能绑定几个微信账号(一个手机号注册多个微信号的方法)...
- 基于银河麒麟 V10 系统安装和卸载 DM8 数据库
- 测试两台计算机网络情况,使用iperf测试两台电脑之间的网速
- 怎样才能画好古代汉服?画好古代汉服有哪些技巧?
- iphone设置邮箱设置_如何使用iPhone设置Chromecast
- asp.net实现动态显示当前日期时间
- 神调侃!程序员专属成长书单,我比女朋友更了解你!
- 2019~2020数字货币领域发展趋势报告