多用组合少用继承的设计模式JAVA_结合设计模式理解多用组合少用继承的原则(转)...
刚刚接触模式或者学习模式的人,经常会有这样的问题,为什么模式是成功的呢?很多人都会说模式是经验的积累,当然是正确的。可是经验为什么偏偏就证明了这
种模式是正确的呢?这其中起用作的就是面向对象的基本原则。正是因为模式都或多或少的符合了面向对象的基本原则,所以模式才成为我们面向对象的设计和编码
过程中不败的法则。那么什么是面向对象的基本原则呢?这就是我们将要一一讲到的问题。
单纯的讲到一个个的原则,就是那么的寥寥几句,非常的简单,但又是非常抽象的,难以理解。怎么办?
任何的理论,只要有生动的例子来讲解或证明,就能极大的帮助理解。所以我们准备从一个个的生动的例子来阐述我们的面向对象的基本原则。讲那些例子呢?上面我们说到,模式都是极大的遵从了这些原则的,那么我们把模式作为例子,来说明这些原则,不是我们信手拈来的吗?
现在我们说说其中的一个原则:对类的功能的扩展,要多用组合,少用继承。
对于类的扩展,在面向对象的编程过程中,我们首先想到的是类的继承,由子类继承父类,从而完成了对子类功能的扩展。但是,面向对象的原则告诉我们,对类的功能的扩展要多用组合,而少用继承。其中的原因有以下几点:
第一、子类对父类的继承是全部的公有和受保护的继承,这使得子类可能继承了对子类无用甚至有害的父类的方法。换句话说,子类只希望继承父类的一部分方法,怎么办?
第二、实际的对象千变万化,如果每一类的对象都有他们自己的类,尽管这些类都继承了他们的父类,但有些时候还是会造成类的无限膨胀。
第三、 继承的子类,实际上需要编译期确定下来,这满足不了需要在运行内才能确定对象的情况。而组合却可以比继承灵活得多,可以在运行期才决定某个对象。
嗨!光说这么多一二三有什么用,我们就是想看看实际情况是不是像上面说的那样呢?还是来看看实际的例子吧!
现在我们需要这样一个HashMap,它除了能按常规的Map那样取值,如get(Object obj)。还能按位取值,像ArrayList那样,按存入对象对的先后顺序取值。
对于这样一个问题,我们首先想到的是做一个类,它继承了HashMap类,然后用一个ArrayList属性来保存存入的key,我们按key的位来取值,代码如下:
Java代码
publicclassListMapextendsHashMap {
privateList list;
publicListMap() {
super();
this.list =newArrayList();
}
publicObject put(Object key,Object value)
{
if(list.contains(key))
{
list.remove(key);
}
this.list.add(key);
returnsuper.put(key,value);
}
publicObject getKey(inti)
{
returnthis.list.get(i);
}
publicObject getValue(inti)
{
returnthis.get(getKey(i));
}
publicintsize()
{
returnthis.list.size();
}
}
这个ListMap类对HashMap作了一定的扩展,很简单就实现了上面我们所要求的功能。然后我们对该类做一下测试:
ListMap map = newListMap();
map.put("a","111");
map.put("v","190");
map.put("d","132");
for(inti=0;i
{
System.out.println(map.getValue(i));
}
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
{
System.out.println(map.getValue(i));
}
测试结果为:
111
190
132
正是我们所需要看到的结果。如此说来,这个ListMap类就可以放心的使用了吗?有实现了这样功能的类,你的同事或朋友也可能把这个类拿来使用一下,他可能写出来如下的代码:
Java代码
ListMap map =newListMap();
map.put("a","111");
map.put("v","190");
map.put("d","132");
String[] list = (String[])map.values().toArray(newString[0]);
for(inti=0;i
{
System.out.println(list[i]);
}
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
{
System.out.println(list[i]);
}
运行的结果如下:
132
111
190
哎哟,怎么回事啊?与上面的顺序不对了。你朋友过来找你,说你写的代码怎么不对啊?你很吃惊,说把代码给我看看。于是你看到了上面的代码。你大骂道,混蛋,怎么不是用我的getValue方法啊?你朋友搔搔头道,values方法不是一样的吗?你也没告诉我不能用啊?
通过上面的例子,我们看到了继承的第一个危害:继承不分青红皂白的把父类的公有和受保护的方法统统继承下来。如果你的子类没有对一些方法重写,就
会对你的子类产生危害。上面的ListMap类,你没有重写继承自HashMap类的values方法,而该方法仍然是按HashMap的方式取值,没有
先后顺序。这时候,如果在ListMap类的对象里使用该方法取得的值,就没有实现我们上面的要求。
接上面的那个例子,你听了朋友的抱怨,摇摇头,想想也是,不能怪他。你只得把values方法在ListMap类重写一遍,然后又嘀咕着,我是不是该把HashMap类的公有方法在ListMap类里全部重写?很多方法根本没有必要用到啊?……
对了,很多方法在ListMap里根本不必用到,但是你用继承的话,还不得不在ListMap里重写它们。如果用组合的话,就没有上面的烦恼了:
Java代码
publicclassMyListMap {
privateHashMap map;
privateList list;
publicMyListMap()
{
this.map =newHashMap();
this.list =newArrayList();
}
publicObject put(Object key,Object value)
{
if(list.contains(key))
{
list.remove(key);
}
this.list.add(key);
returnthis.map.put(key,value);
}
publicObject getKey(inti)
{
returnthis.list.get(i);
}
publicObject getValue(inti)
{
returnthis.map.get(getKey(i));
}
publicintsize()
{
returnthis.list.size();
}
}
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代码
publicinterfaceWhatIHave
{
publicvoidg();
}
publicclassResource
{
publicvoidf()
{
……
}
publicvoidh()
{
……
}
}
public interface WhatIHave
{
public void g();
}
public class Resource
{
public void f()
{
……
}
public void h()
{
……
}
}
上面是两个基础类,很明显,我们所要的类既要有g()方法,也要有f()和h()方法。
Java代码
PublicclassWhatIWantimplementsWhatIHave
{
privateResource res;
publicWhatIWant()
{
res = newResource();
}
publicvoidg()
{
……
}
publicvoidf()
{
this.res.f();
}
publicvoidh()
{
this.res.h();
}
}
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代码
packagedecorator;
interfaceProduct {
publicdoublemoney();
}
//咖啡类:
classCoffeeimplementsProduct {
publicdoublemoney() {
return12;
}
}
//加糖:
classSugarimplementsProduct {
privateProduct product;
publicSugar(Product product) {
this.product = product;
}
publicdoublemoney() {
returnproduct.money() +2;
}
}
//加冰:
classIceimplementsProduct {
privateProduct product;
publicIce(Product product) {
this.product = product;
}
publicdoublemoney() {
returnproduct.money() +1.5;
}
}
//加奶:
classMilkimplementsProduct {
privateProduct product;
publicMilk(Product product) {
this.product = product;
}
publicdoublemoney() {
returnproduct.money() +4.0;
}
}
//加巧克力:
classChocolateimplementsProduct {
privateProduct product;
publicChocolate(Product product) {
this.product = product;
}
publicdoublemoney() {
returnproduct.money() +5.5;
}
}
publicclassDecoratorModel{
publicstaticvoidmain(String [] args){
Product coffee = newCoffee();
Product sugarCoffee = newSugar(coffee);
Product sugarmilkCoffee = newMilk(sugarCoffee);
System.out.println("加糖咖啡:"+sugarCoffee.money());
System.out.println("加糖加奶咖啡:"+sugarmilkCoffee.money());
}
}
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_结合设计模式理解多用组合少用继承的原则(转)...相关推荐
- JavaScript面向对象——深入理解寄生组合继承
JavaScript面向对象--深入理解寄生组合继承 之前谈到过组合继承,会有初始化两次实例方法/属性的缺点,接下来我们谈谈为了避免这种缺点的寄生组合继承 寄生组合继承: 思路:组合继承中,构造函数继 ...
- 设计模式学习笔记(十一)-组合模式
树形结构在软件中随处可见,例如操作系统中的目录结构.应用软件中的菜单.办公系统中的公司组织结构等等,如何运用面向对象的方式来处理这种树形结构是组合模式需要解决的问题,组合模式通过一种巧妙的设计方案使得 ...
- Python零基础速成班-第9讲-Python面向对象编程(上),对象和类、初始化、继承、重写、多态、类方法、组合
Python零基础速成班-第9讲-Python面向对象编程(上),对象和类.初始化.继承.重写.多态.类方法.组合 学习目标 修饰器 面向对象编程:对象和类.初始化.继承.重写.多态.类方法.组合 课 ...
- 【JavaScript设计模式张容铭】抽象工厂模式深度剖析——关于继承与constructor属性的深度理解
写在前面 最近阅读了张容铭的<JavaScript设计模式>一书,阅读到抽象工厂模式一节时对书上的代码产生了疑问,但同时在解决疑问的过程中,对继承又产生了新的理解. 我仔细查阅了很多文章, ...
- 【26天高效学完Java编程】Day11:继承、抽象类、模板设计模式详解
本专栏将从基础开始,循序渐进,由浅入深讲解Java的基本使用,希望大家都能够从中有所收获,也请大家多多支持. 专栏地址:26天高效学习Java编程 相关软件地址:软件地址 所有代码地址:代码地址 如果 ...
- JavaScript学习笔记(四)---闭包、递归、柯里化函数、继承、深浅拷贝、设计模式
JavaScript学习笔记(四)---闭包.递归.柯里化函数.继承.深浅拷贝.设计模式 1. 匿名函数的使用场景 2.自运行 3.闭包 3.1前提: 3.2闭包 4.函数对象的三种定义方式 5.th ...
- 【设计模式自习室】结构型:组合模式 Composite
前言 <设计模式自习室>系列,顾名思义,本系列文章带你温习常见的设计模式.主要内容有: 该模式的介绍,包括: 引子.意图(大白话解释) 类图.时序图(理论规范) 该模式的代码示例:熟悉该模 ...
- 设计模式总结之Composite Pattern(组合模式)
Composite Pattern(组合模式) 组合模式,将对象组合成树形结构以表示"部分-整体"的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性. 有时候又叫做部 ...
- [Head First设计模式]生活中学设计模式——组合模式
系列文章 [Head First设计模式]山西面馆中的设计模式--装饰者模式 [Head First设计模式]山西面馆中的设计模式--观察者模式 [Head First设计模式]山西面馆中的设计模式- ...
- python中继承和组合的区别_Py修行路 python基础 (十五)面向对象编程 继承 组合 接口和抽象类...
一.前提回忆: 1.类是用来描述某一类的事物,类的对象就是这一类事物中的一个个体.是事物就要有属性,属性分为 1:数据属性:就是变量 2:函数属性:就是函数,在面向对象里通常称为方法 注意:类和对象均 ...
最新文章
- iOS 时间选择器封装(含三种模式)
- 【深度学习】模型训练教程之Focal Loss调参和Dice实现
- python distutils模块(貌似是用来打包发布自定义python包的)
- 快搜浏览器_郑秀晶因腿粗再上热搜:怎么减肥才能不反弹?
- window.open被浏览器拦截的解决方案
- 深度学习——用神经网络解决非线性问题
- pythonshell窗口是什么_python与shell的3种交互方式介绍
- 【原创】关于Git暂存区的理解
- Eclipse Java EE的tomcat使用小结
- matlab的v带优化设计,基于遗传算法及MATLAB的V带传动优化设计
- 数据库 备份 压缩
- 计算机基础(1)——Verilog语法入门
- HTMLTestRunnerNew 测试报告详解
- 用免费WiFi上网软件有什么好处
- 不联网服务器系统时间,电脑时间不准确联网自动调整步骤
- BP反向传播算法原理及公式推导
- Chapter 1 (Linear Equations in Linear Algebra): System of linear equations (线性方程组)
- uniapp返回上一页并刷新数据
- 【整理】输入导航功能-查询与拼音首字母的结合以提高用户体验
- MSP430待机功耗问题
热门文章
- 2020年阴历三月初九投资理财~从牛人那里吸取能量,让自己更加强大
- IT服务台基础概念及创建方法
- html中图片旋转木马,教你怎么用CSS3做一个图片的旋转木马效果
- www.skymaya.com:8080/index.php,www_skymaya_com
- python入门到精通 _6文件读写
- hdu5294Tricks Device【最短路+网络流】
- 教师-学生网络下Self-Ensembing with GAN-based Data Augmentation for Domain Adaption in Sematic Segmentation
- 科普知识:深网和暗网
- linux系统玩什么游戏,linux系统可以玩什么网游
- 三月月赛 1005 wuli通通和doge(细节处理)