善于思考,方法总比问题多!

故事背景

我和漂亮的模式MM来到巴厘岛,这里火山爆发刚刚结束不久,一切要重新开始,来到这个地方几天后,觉得这个地方还是不错,于是就决定在这里开一个奶茶店,因为这里游客比较多,流量大,反正之前我们也没有开店的体验,那么一拍即合,开个奶茶店,体验一下了。

奶茶店的名字:Beautiful Life milk tea!

名字起好了,那么我们就开始想如何做一个奶茶收费的系统,让我们在卖奶茶的时候帮我们自动计费!因为在购买奶茶的时候,可以在奶茶中加入各种调料,如,绿茶粉、红茶粉,果冻、珍珠等,然后根据加入的东西不同费用也不同!

第一版我进行了简单的设计,代码如下:

public abstract class MilkTea {private String name;//名字public String getName(){return name;}//支付的钱public abstract double cost();
}public class GreenTea extends MilkTea {@Overridepublic double cost() {return 10.0;}
}
public class RedTea extends MilkTea {public RedTea(String name){super.setName(name);}@Overridepublic double cost() {return 15.0;}
}public class GuoDongGreenTea extends MilkTea {public GuoDongGreenTea(String name){super.setName(name);}@Overridepublic double cost() {//果冻绿茶的价钱= 果冻价钱 + 绿茶价钱return 3.0 + 10.0;}
}// 其他的种类不在一一列举了。
public class Client {public static void main(String[] args) {//这里有一个顾客,首先点了一个红茶,然后点了一个 果冻绿茶MilkTea tea = new RedTea("红茶");double cost = tea.cost();System.out.println(tea.getName() + "需要支付:" + cost);tea = new GuoDongGreenTea("果冻奶茶");cost = tea.cost();System.out.println(tea.getName() + "需要支付:" + cost);}
}红茶需要支付:15.0
果冻奶茶需要支付:13.0

设计模式MM : 这种方式是很简单,也能实现我们的需求,但是每一种奶茶都需要一个类,如果业务发展快,新开发了很多种类的奶茶的话,那么系统的类就会多到啊爆炸!还有如果某一天珍珠或者果冻降价了,那会出现所有和珍珠或者果冻有关的奶茶都需要修改!
如图:

因为第一版会存在类爆炸问题,所以我又思考了第二版,

/*** 第二版的实现*/
public class MilkTea {private String name;//名字Boolean hasGuoDong = false; //是否添加果冻Boolean hasZhenZhu = false; //是否添加珍珠public String getName(){return name;}public void setName(String name) {this.name = name;}public Boolean getHasGuoDong() {return hasGuoDong;}public void setHasGuoDong(Boolean hasGuoDong) {this.hasGuoDong = hasGuoDong;}public Boolean getHasZhenZhu() {return hasZhenZhu;}public void setHasZhenZhu(Boolean hasZhenZhu) {this.hasZhenZhu = hasZhenZhu;}//支付的钱public double cost(){double cost = 0;if(hasGuoDong){cost += 3.0;}else if(hasZhenZhu){cost += 5.0;}else {cost += 0.0;}return cost;}}//绿茶
public class GreenTea extends MilkTea {public GreenTea(String name){super.setName(name);}@Overridepublic double cost() {//添加的东西价钱 + 绿茶的价钱return super.cost() + 10.0;}
}//红茶
public class RedTea extends MilkTea{public RedTea(String name){super.setName(name);}@Overridepublic double cost() {//添加的东西价钱 + 红茶的价钱return super.cost() + 15.0;}
}public class Client {public static void main(String[] args) {//这里有一个顾客,首先点了一个果冻红茶,然后点了一个珍珠绿茶MilkTea tea = new RedTea("果冻红茶");tea.setHasGuoDong(true);double cost = tea.cost();System.out.println(tea.getName() + "需要支付:" + cost);tea = new GreenTea("珍珠绿茶");tea.setHasZhenZhu(true);cost = tea.cost();System.out.println(tea.getName() + "需要支付:" + cost);}
}果冻红茶需要支付:18.0
珍珠绿茶需要支付:15.0

设计模式MM : 这种方式使用继承的关系在超类里面定义各种不同添加物的价钱,使用子类去继承,这样可以防止第一版中的类爆炸问题和如果一个添加物的价格发生变化的话,只需要修改一个地方!但是你上面的实现还是违反了我的原则(设计模式原则):开闭原则!

  • 如果添加物的价格改变要改现有的超类代码
  • 如果有新的添加物要加入的话,还是要改现有的超类代码
  • 万一顾客想要在一杯奶茶中加上两倍的果冻,或者珍珠呢,那么又应该怎么做呢?
  • …..

设计模式MM:上面两个版本虽然在一定程度上满足需求,但是不是很好的设计方案,我给你提供一个建议,去看看装饰者模式吧!给爱用继承的你一个全新的设计眼界。

故事主角—装饰者模式

装饰者模式 : 动态的将责任附加到对象上。若要扩展功能,装饰者提供比继承更有弹性的替代方案!

注:使用继承的话,编译时候决定了子类都会继承到相同的行为。使用 组合,可以在运行时候动态的扩展对象的行为。

在装饰模式中,为了让系统具有更好的灵活性和可扩展性,我们通常会定义一个抽象的装饰类。而具体的装饰类作为它的子类,

在装饰者类图中,有一下角色登场。
- Component(抽象构件):抽象构件是具体构件和抽象装饰类的共同父类,申明了在具体构件中实现的业务方法,它的引入可以使客户端以一致的方式处理为被装饰的对象以及装饰之后的对象,实现客户端的透明操作!(装饰物和被装饰物的一致性)

  • ConcreteComponent(具体构件):抽象构件类的子类,实现父类申明方法定义具体的构件对象。(装饰模式中的被装饰者)

  • Decorator(抽象装饰类):抽象构件类的子类,用户给具体构件增加功能或职责。它维护了一个抽象构件对象的引用,通过引用可以调用到装饰之前构件对象的方法。通过子类扩展改方法,在不改变具体构件的情况下,达到装饰目的。(抽象装饰类Decorator并不是必须的)

  • ConcreteDecorator(具体装饰类):抽象装饰类的子类,负责向具体构件添加职责。每一个具体的装饰类定义一些新的行为,扩充具体构件对象的行为。

下面用简单代码诠释一下:

//抽象构件,使用一个接口定义
public interface Component{void method1();
}//具体的构件,即被装饰对象
public class ConcreteComponent implments Component{public void method1(){//do something...}
}//定义抽象装饰类,这个类不是必须的,但是为了扩展更多的装饰物,对装饰物进行抽象
public abstract class Decorator implments Component{private Component component;public Decorator(Component component){this.component = component;}public void method1(){component.method1();}
}//定义具体的装饰类 X,Y和X一样就写出了public class ConcreteDecoratorX extends Decorator{public ConcreteDecoratorX(Component component){super(component);}public void methodX(){//使用methodX进行包装/装饰}public void method1(){super.method1();methodX();}}

由于在抽象装饰类Decorator中注入的是Component类型的对象,因此我们可以将一个具体构件对象注入其中,再通过具体装饰类来进行装饰;此外,我们还可以将一个已经装饰过的Decorator子类的对象再注入其中进行多次装饰,从而对原有功能的多次扩展。

武功修炼

根据上面对装饰者模式的讲解,说白了我们之前的奶茶是被装饰物,装饰物就是 果冻或者珍珠等等添加物。下面使用装饰者模式来进行设计。

/*** 奶茶店最终设计方案,抽象构件* @author:dufyun* @version:1.0.0**/
public interface MilkTea {/*** 奶茶的名字* @return*/String getName();/*** 奶茶的价格* @return*/double cost();
}/*** 红茶类 --具体的构件,被装饰对象** @author:dufyun* @version:1.0.0*/public class RedTea implements MilkTea{/*** 奶茶名字*/private static String name = "红茶";public RedTea(){}@Overridepublic String getName() {return name;}@Overridepublic double cost() {return 15.0;}
}/*** 绿茶类 --具体的构件,被装饰对象* @author:dufyun* @version:1.0.0*/
public class GreenTea implements MilkTea {/*** 奶茶名字*/private static String name = "绿茶";public GreenTea(){}@Overridepublic String getName() {return name;}@Overridepublic double cost() {return 10.0;}
}/***  抽象装饰类** @author:dufyun* @version:1.0.0*/
public abstract class MilkTeaDecorator implements MilkTea {private MilkTea milkTea;public MilkTeaDecorator(MilkTea milkTea){this.milkTea = milkTea;}@Overridepublic String getName(){return milkTea.getName();}@Overridepublic double cost() {return milkTea.cost();}public abstract void addOtherMetod();
}/*** 果冻类-具体的装饰者,具体装饰构件** @author:dufyun* @version:1.0.0*/
public class GuoDong extends MilkTeaDecorator {private static String name = "果冻";public GuoDong(MilkTea milkTea){super(milkTea);}@Overridepublic String getName() {return super.getName()+ ":" + name;}@Overridepublic double cost() {return 3.0 + super.cost();}@Overridepublic void addOtherMetod() {System.out.println("其他的增强方法");}}
/*** 珍珠类-具体的装饰者,具体装饰构件** @author:dufyun* @version:1.0.0*/
public class ZhenZhu extends MilkTeaDecorator {private static String name = "珍珠";public ZhenZhu(MilkTea milkTea){super(milkTea);}@Overridepublic String getName() {return super.getName()+ ":" + name;}@Overridepublic double cost() {return 5.0 + super.cost();}@Overridepublic void addOtherMetod() {System.out.println("其他的增强方法");}}
public class Client {public static void main(String[] args) {//绿茶 10.0 红茶 15.0 果冻一份 3.0 珍珠一份5.0//这里有一个顾客A,首先点了一杯红茶,然后需要加一份果冻和一份珍珠//被装饰者在最内层MilkTea teaA = new GuoDong(new ZhenZhu(new RedTea()));String nameA = teaA.getName();double costA = teaA.cost();System.out.println(nameA  + " : " + costA);//这里有一个顾客B,首先点了一杯绿茶,然后需要加两份份果冻和三份珍珠MilkTea teaB = new GuoDong(new GuoDong( //两份果冻new ZhenZhu(new ZhenZhu(new ZhenZhu(//三份珍珠new GreenTea())))));String nameB = teaB.getName();double costB = teaB.cost();System.out.println(nameB + " : " + costB);}
}红茶:珍珠:果冻 : 23.0
绿茶:珍珠:珍珠:珍珠:果冻:果冻 : 31.0
  • 我: 设计模式mm。你看我这次实现的这个代码怎么样,我根据设计模式装饰者模式进行实现。
  • 设计模式MM:这个实现比之前的好很多了,既不会出现类的迅速爆炸,也符合设计模式原则:开闭原则,如果新增装饰物业不用修改之前的代码。并且使用组合方式,动态的扩展对象行为,也能很好解决上面说的如果一个顾客想要两份果冻和珍珠的问题。这种设计使得系统扩展更加方便,也更容易维护,很棒!不过需要注意的是这种方式也会有很多小类,类的数量也会很多!

武功深入—Java IO中的装饰者模式

通过对装饰者的学习,发现在Java中的IO有使用到装饰者模式,那么下面我们对象Java IO 中的装饰者模式进行简单的学习。

这里只拿出Java InputStream的简单类图介绍,OutputStream等类似。(JavaIO中的输入流和输出流的设计)

  • 其中 InputStream 为抽象构件,FileInputStream、ByteArrayInputStream、ObjectInputStream等是具体构件实现。
  • FilterInputStream为抽象装饰类,LineInputStream、BufferedInputStream等是具体的装饰实现!
//一般在写InputStream时候
InputStream in = new BufferedInputStream(new FileInputStream(new File("d:/test.txt")));

根据对装饰者的学习,和Java Io类图的了解,编辑一个自己的输入装饰者:把输入流中的所有小写字符转成大写字符。

text.txt文件中的内容Do you love Pattern Design!Yes ! I love!/*** 自定义实现输入流程中小写字符转大写字符** @author:dufyun* @version:1.0.0*/
public class UpperCaseInputStream extends FilterInputStream {/*** Creates a <code>FilterInputStream</code>* by assigning the  argument <code>in</code>* to the field <code>this.in</code> so as* to remember it for later use.** @param in the underlying input stream, or <code>null</code> if*           this instance is to be created without an underlying stream.*/protected UpperCaseInputStream(InputStream in) {super(in);}@Overridepublic int read() throws IOException {int c = super.read();return (c == -1 ? c :Character.toUpperCase((char) c));}@Overridepublic int read(byte[] b, int off, int len) throws IOException {int rs = super.read(b, off, len);for (int i = off; i < off + rs; i++) {b[i] = (byte)Character.toUpperCase((char) b[i]);}return rs;}//test 方法 ,不建议写main进行测试public static void main(String[] args) {try {InputStream in = new UpperCaseInputStream(new BufferedInputStream(new FileInputStream(new File("d:/test.txt")))) ;int c;while ( (c = in.read()) >= 0){System.out.print((char)c);}} catch (Exception e) {e.printStackTrace();}}
}打印结果:DO YOU LOVE PATTERN DESIGN!YES ! I LOVE!

java Io使用装饰者模式,也有一个“缺点”,就是在设计中会有大量的小类,数量实在太多。但是不要怕,如果你了解 了装饰者模式那么很容易辨识出装饰类是如何组织的了。

故事结局 — 装饰者模式总结

通过上面对装饰者模式的介绍,总体上对装饰者模式有了一个整体的认识。装饰者模式降低系统的耦合度,可以动态的增加或删除对象的职责,并使得需要装饰的具体构件类和具体装饰类独立变化。

优点
- 对于扩展一个对象功能,装饰模式比继承更加灵活,并且不会导致类个数急剧增加。
- 通过一种动态的方式来扩展一个对象的功能,可以使用配置文件在运行时选择不同的具体装饰者,实现不同的行为。
- 可以对一个对象进行多次装饰,可以使用不同的排列组合,能够创建很多不同的行为,得到功能更强大的对象。
- 具体的构件和装饰类可以独立变化,当新增具体构件或者具体装饰类,原有类库代码无须修改,符合“开闭原则”

缺点
- 在对系统进行设计的时候,会产生很多的小对象,大量的小对象产生会占用更多的系统资源,在一定程度上影响程序性能。。
- 提供一种比继承更加灵活的解决方案,但同时意味着比继承更容易出错,排错也困难。

使用场景
- 在不影响其他对象的情况下,以动态的、透明的方式给单个对象添加职责。
- 当不能采用继承的方式对系统进行扩展或者采用继承不利于系统扩展的时候可以使用装饰者模式。

Next 期待下一篇吧!完成了奶茶店奶茶如何高效的实现计费,我们的奶茶店总算经过一系列努力开了起来,生意很红火,钞票不断的进入口袋。于是扩大店面,进行了装修转了很多的酷炫灯,但是遇到一个问题,如何使用遥控器控制所有的灯呢!下一篇:命令模式,封装调用。使用命令控制奶茶店中酷炫的灯!

参考

  • 史上最全设计模式导学
  • 《Head First 设计模式》

本专栏文章列表

一、设计模式-开篇—为什么我要去旅行? #和设计模式一起旅行#
二、设计模式-必要的基础知识—旅行前的准备 #和设计模式一起旅行#
三、设计模式介绍—她是谁,我们要去哪里? #和设计模式一起旅行#
四、单例模式—不要冒充我,我只有一个! #和设计模式一起旅行#
五、工厂模式—旅行的钱怎么来 #和设计模式一起旅行#
六、策略模式—旅行的交通工具 #和设计模式一起旅行#
七、观察者模式——关注我,分享旅途最浪漫的瞬间! #和设计模式一起旅行#
八、装饰者模式—巴厘岛,奶茶店的困扰! #和设计模式一起旅行#
九、命令模式—使用命令控制奶茶店中酷炫的灯 #和设计模式一起旅行#
十、模板方法模式—制作更多好喝的饮品! #和设计模式一起旅行#
十一、代理模式 —专注,做最好的自己!#和设计模式一起旅行#
十二、适配器模式——解决充电的烦恼 #和设计模式一起旅行#
十三、外观模式—— 简化接口 #和设计模式一起旅行#
十四、迭代器模式—— 一个一个的遍历 #和设计模式一起旅行#
十五、组合模式—— 容器与内容的一致性 #和设计模式一起旅行#
十六、状态模式—用类表示状态 #和设计模式一起旅行#
十七、访问者模式-访问数据结构并处理数据 #和设计模式一起旅行#
十八、职责链模式-推卸责任,不关我的事,我不管!#和设计模式一起旅行#
十九、原型模式—通过复制生产实例 #和设计模式一起旅行#
二十、设计模式总结—后会有期 #和设计模式一起旅行#


如果您觉得这篇博文对你有帮助,请点赞或者喜欢,让更多的人看到,谢谢!

如果帅气(美丽)、睿智(聪颖),和我一样简单善良的你看到本篇博文中存在问题,请指出,我虚心接受你让我成长的批评,谢谢阅读!
祝你今天开心愉快!


欢迎访问我的csdn博客,我们一同成长!

不管做什么,只要坚持下去就会看到不一样!在路上,不卑不亢!

博客首页 : http://blog.csdn.net/u010648555

八、装饰者模式—巴厘岛,奶茶店的困扰! #和设计模式一起旅行#相关推荐

  1. 详解设计模式之装饰者模式[以奶茶为案例介绍]

    背景案例介绍 以奶茶为例,如下图所示,Beverage(饮料)是一个抽象类,类里面有一个description(描述)实例用来描述该奶茶,可以描述出奶茶的分类,奶茶有很多种分类,比如抹茶,波霸,玛奇朵 ...

  2. php设计模式八-----装饰器模式

    1.介绍: 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰 ...

  3. java装饰者模式服装搭配,学习、探究Java设计模式——装饰者模式

    定义 装饰者模式:在不改变原类文件以及不使用继承的情况下,动态地将责任附加到对象上,从而实现动态拓展一个对象的功能.它是通过创建一个包装对象,也就是装饰来包裹真实的对象. 设计原则 要使用装饰者模式, ...

  4. 米线店结账程序 装饰着模式_米线店整体装修设计方案图 4款云南过桥米线加盟店47平米室内设计图...

    米线店整体装修设计方案图 4款云南过桥米线加盟店47平米室内设计图 云南过桥米线加盟店内部的布置设计散发着中式的气息,宽敞的内部空间很有特色,顶棚石膏天花板无吊顶铺设很规整,再加上方形的长条灯池搭配, ...

  5. 米线店结账程序 装饰着模式_实验报告2_装饰者模式

    序号: 姓名: 杨林燕 学号: 106 专业: 软件工程 日期: 成绩: 实验二 装饰者模式的运用 一.实验目的: 装饰者模式动态地将责任附加到对象上,若要扩展功能,装饰者提供了比继承更有弹 性的替代 ...

  6. 米线店结账程序 装饰着模式_装饰者模式的运用

    实 验 报 告 实验二 装饰者模式的运用 一.实验目的: 装饰者模式动态地将责任附加到对象上,若要扩展功能,装饰者提供了比继承更有弹 性的替代方案.在熟悉装饰者模式相关理论知识的基础上,使用装设者模式 ...

  7. 十八、职责链模式-推卸责任,不关我的事,我不管!#和设计模式一起旅行#

    不在其位,不谋其政! –出自<论语·泰伯> 故事背景 在现实世界中,有很多情况下会遇到一些推卸责任的场景,比如要办理一件事的时候,被告诉你要去做个做这个事情,但是去了这个地方,确告诉要到另 ...

  8. 浅析设计模式3 —— 装饰者模式

    推荐语:本文从装饰者模式的核心思想到与其他设计模式的横向对比,从代码示例到业务实战,向读者娓娓呈现装饰者模式的真貌.深入浅出的JDK源码透析,使用场景的利弊权衡,真的值得一阅! --大淘宝技术开发工程 ...

  9. 吃透设计模式第六篇-装饰者模式

    设计模式的重要性对于程序员来说,相当于盾牌对于美国队长,暴风战斧相对于雷神,内裤对于绿巨人(绿巨人最强武器,手动狗头)来说,是必不可少的. 在此,特别总结下23钟设计模式: 创建型模式:单例模式.抽象 ...

最新文章

  1. 人物丨深度学习大神Hinton推翻自己30年的学术成果另造新世界
  2. 201521123029《Java程序设计》第1周学习总结
  3. nginx配置技巧汇总
  4. RESTful API的理解
  5. SpringMVC4返回json
  6. 中的实践 中兴_中兴通讯5G智慧治水业务在千岛湖畔下姜村成功实践
  7. mysql 数据库连接不够_一个Web报表项目的性能分析和优化实践(二):MySQL数据库连接不够用(TooManyConnections)问题的一次分析和解决案例...
  8. Linux进程调度原理【转】
  9. cocostudio html5,Cocostudio的简单使用:
  10. win10安装SqlServer2005教程
  11. 74LS165芯片单颗与级联的万能代码|2021-10-6
  12. 如何实现中文汉字进行笔划(笔画)排序?
  13. 系统端口被占用解决方法
  14. 北京大学陈波悖论课程-期末测试-解析
  15. 如何判断电脑电源故障
  16. linux下三台虚拟机互联,Linux下虚拟机qemu的使用
  17. 10.JAVA中的集合(数据结构)
  18. 自动驾驶目标识别-----毫米波雷达学习笔记(1)
  19. 蓝牙、红外线与wifi 区别以及不同频段无线电磁波的穿墙和绕过障碍物能力(转)
  20. ozon平台刊登上架很复杂?有哪些免费ERP系统支持呢

热门文章

  1. 快速阅读的方法与策略
  2. python爬虫抖音音浪_抖音音浪怎么获得 抖音音浪怎么来的
  3. RFID养老院人员定位解决方案浅析,你不知道的智慧养老-新导智能
  4. 防沉迷解除网站php,游戏防沉迷系统名存实亡?
  5. 三网合一短信平台企业自己可以弄吗?三网合一短信是否可以统一显示号码
  6. 初一年级英语口语测试软件,七年级英语口语测试模拟试题.doc
  7. halcon第二十三讲:标定助手标定测量
  8. uni-app+企业微信+用友U9ERP搭建H5应用(1)
  9. System Design [youtube搬运] Tinder 笔记
  10. 文献阅读(194)Multi-packet Bypassing