首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

设计模式 —— 装饰者模式

  • 25-03-07 20:22
  • 2642
  • 8049
blog.csdn.net

目录

一、星巴克咖啡订单项目需求

二、方案 1-解决星巴克咖啡订单项目(较差)

三、方案 2-解决星巴克咖啡订单(好点)

四、装饰者模式定义

4.1 装饰者模式原理

4.2 装饰者模式解决星巴克咖啡订单

五、装饰者模式在 JDK 应用的源码分析 

 六、装饰模式的应用

6.1 装饰者模式何时使用

6.2 装饰者模式方法

5.3 装饰者模式优点

6.4 装饰者模式缺点

6.5 装饰者模式使用场景

6.6 装饰者模式应用实例

七、大白话小结


装饰者模式定义:

装饰模式(Decorator),动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更灵活

初计初衷:

通常可以使用继承来实现功能的扩展,如果这些需要扩展的功能的种类很繁多,那么势必生成很多子类,增加系统的复杂性,同时使用继承实现功能拓展,我们必须可预见这些拓展功能,这些功能是编译时就确定了,是静态的。

要点:

装饰者与被装饰者拥有共同的超类,继承的目的是继承类型,而不是行为

装饰者模式的设计原则

对扩展开放、对修改关闭,这句话体现我如果想扩展被装饰者类的行为,无须修改装饰者抽象类,只需要继承装饰者抽象类,实现额外的一些装饰或者行为即可对被装饰者进行包装。所以:扩展体现在继承、修改体现在子类中,而不是具体的抽象类,这充分体现了依赖倒置原则,这是自己理解的装饰者模式。


大白话小结

继承可实现功能的扩展,但如果需要拓展的功能的种类很繁多(KFC肯得基订单需求),那么势必生成很多子类。装饰者模式通过:装饰者类内部含有被装饰者(组合关系),且被装饰者与装饰者都继承自共同的父类。这样可以通过将被装饰者的子类实例对象  传入-> 装饰者子类的实例对象中,拓展被装饰者继承类即可实现动态的将新功能 附加到装饰者子类实例对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp)。

 


一、星巴克咖啡订单项目需求

  1. 咖啡种类/单品咖啡:Espresso(意大利浓咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(无因咖啡)
  2. 调料:Milk、Soy(豆浆)、Chocolate
  3. 要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便
  4. 使用 OOP 面象对象 的来计算不同种类咖啡的费用:  客户可以点单品咖啡,也可以单品咖啡+调料组合

二、方案 1-解决星巴克咖啡订单项目(较差)

2.1 传统方案解决星巴克咖啡订单项目UML类图

较差方案:

 2.2 方案 1-解决星巴克咖啡订单问题分析

 

  1. Drink 是一个抽象类,表示饮料
  2. des 就是对咖啡的描述, 比如咖啡的名字
  3. cost() 方法就是计算费用,Drink 类中做成一个抽象方法.
  4. Decaf 就是单品咖啡, 继承 Drink,  并实现 cost
  5. Espress && Milk 就是单品咖啡+调料, 这个组合很多

问题:这样设计,会有很多类,当我们增加一个单品咖啡,或者一个新的调料,类的数量就会倍增,就会出现类爆炸


三、方案 2-解决星巴克咖啡订单(好点)

前面分析到方案 1 因为咖啡单品+调料组合会造成类的倍增,因此可以做改进,将调料内置到Drink 类,这样就不会造成类数量过多。从而提高项目的维护性(如图)

方案 2-解决星巴克咖啡订单问题分析

  1. 方案 2 可以控制类的数量,不至于造成很多的类
  2. 在增加或者删除调料种类时,代码的维护量很大
  3. 考虑到用户可以添加多份 调料时,可以将 hasMilk 返回一个对应 int
  4. 考虑使用 装饰者 模式

四、装饰者模式定义

  1. 装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp)
  2. 这里提到的动态的将新功能附加到对象和 OCP 原则,在后面的应用实例上会以代码的形式体现。

 

4.1 装饰者模式原理

A、装饰者模式就像打包一个快递

  • 主体:比如:陶瓷、衣服 (Component) //  被装饰者
  • 包装:比如:报纸填充、塑料泡沫、纸板、木板(Decorator)

B、Component 主体:比如类似前面的 Drink

C、ConcreteComponent 和 Decorator

  • ConcreteComponent:具体的主体, 比如前面的各个单品咖啡

D、Decorator: 装饰者,比如各调料

  • 在如图的 Component 与 ConcreteComponent 之间,如果 ConcreteComponent 类很多,还可以设计一个缓冲层,将共有的部分提取出来,抽象层一个类。

 

4.2 装饰者模式解决星巴克咖啡订单

装饰者模式下的订单:2 份巧克力+一份牛奶的 LongBlack

AbstractDrink 源码示例:

  1. public abstract class AbstractDrink {
  2. public String des; // 描述
  3. private float price = 0.0f;
  4. public String getDes() {
  5. return des;
  6. }
  7. public void setDes(String des) {
  8. this.des = des;
  9. }
  10. public float getPrice() {
  11. return price;
  12. }
  13. public void setPrice(float price) {
  14. this.price = price;
  15. }
  16. // 计算费用的抽象方法
  17. // 子类来实现
  18. public abstract float cost();
  19. }

Coffee 源码示例:

  1. public class Coffee extends AbstractDrink {
  2. @Override
  3. public float cost() {
  4. return super.getPrice();
  5. }
  6. }

Espresso 源码示例:

  1. public final class Espresso extends Coffee {
  2. public Espresso() {
  3. setDes(" 意大利咖啡 ");
  4. setPrice(6.0f);
  5. }
  6. }

LongBlack 源码示例:

  1. public final class LongBlack extends Coffee {
  2. public LongBlack() {
  3. setDes(" longBlack ");
  4. setPrice(5.0f);
  5. }
  6. }

ShortBlack 源码示例:

  1. public final class ShortBlack extends Coffee {
  2. public ShortBlack() {
  3. setDes(" shortBlack ");
  4. setPrice(4.0f);
  5. }
  6. }

 Decaf 源码示例:

  1. public class Decaf extends Coffee {
  2. public Decaf() {
  3. setDes(" 无因咖啡 ");
  4. setPrice(1.0f);
  5. }
  6. }

Decorator 源码示例:

  1. public class Decorator extends AbstractDrink {
  2. private AbstractDrink drink;
  3. public Decorator(AbstractDrink drink) { // 组合
  4. this.drink = drink;
  5. }
  6. @Override
  7. public float cost() {
  8. // getPrice 自己价格
  9. return super.getPrice() + drink.cost();
  10. }
  11. @Override
  12. public String getDes() {
  13. // drink.getDes() 输出被装饰者的信息
  14. return super.des + " " + getPrice() + " && " + drink.getDes();
  15. }
  16. }

Chocolate 源码示例:

  1. // 具体的Decorator, 这里就是调味品
  2. public class Chocolate extends Decorator {
  3. public Chocolate(AbstractDrink drink) {
  4. super(drink);
  5. setDes(" 巧克力 ");
  6. setPrice(3.0f); // 调味品的价格
  7. }
  8. }

Milk 源码示例:

  1. public class Milk extends Decorator {
  2. public Milk(AbstractDrink drink) {
  3. super(drink);
  4. setDes(" 牛奶 ");
  5. setPrice(2.0f);
  6. }
  7. }

Soy 源码示例:

  1. public class Soy extends Decorator {
  2. public Soy(AbstractDrink drink) {
  3. super(drink);
  4. setDes(" 豆浆 ");
  5. setPrice(1.5f);
  6. }
  7. }

CoffeeBar 源码示例:

  1. public class CoffeeBar {
  2. public static void main(String[] args) {
  3. // 装饰者模式下的订单:2份巧克力 + 一份牛奶的LongBlack
  4. // 1. 点一份 LongBlack
  5. AbstractDrink order = new LongBlack();
  6. System.out.println("费用1=" + order.getPrice());
  7. System.out.println("描述=" + order.getDes());
  8. // 2. order 加入一份牛奶
  9. order = new Milk(order);
  10. System.out.println("order 加入一份牛奶 费用 = " + order.cost());
  11. System.out.println("order 加入一份牛奶 描述 = " + order.getDes());
  12. // 3. order 加入一份巧克力
  13. order = new Chocolate(order);
  14. System.out.println("order 加入一份巧克力 费用 = " + order.cost());
  15. System.out.println("order 加入一份巧克力 描述 = " + order.getDes());
  16. // 4. order 加入2份巧克力
  17. order = new Chocolate(order);
  18. System.out.println("order 加入2份巧克力 费用 = " + order.cost());
  19. System.out.println("order 加入2份巧克力 描述 = " + order.getDes());
  20. System.out.println("======================================");
  21. AbstractDrink order2 = new Decaf();
  22. System.out.println("order2 无因咖啡 费用 = " + order2.cost());
  23. System.out.println("order2 无因咖啡 描述 = " + order2.getDes());
  24. order2 = new Milk(order2);
  25. System.out.println("order2 无因咖啡 加入一份牛奶 费用 = " + order2.cost());
  26. System.out.println("order2 无因咖啡 加入一份牛奶 描述 = " + order2.getDes());
  27. }
  28. }

运行结果,如下:


五、装饰者模式在 JDK 应用的源码分析 

Java 的 IO 结构,FilterInputStream 就是一个装饰者

  1. package com.java.z_exam.design_patterns.c04_decorator.jdk;
  2. /**
  3. * @Author:
  4. * @Date: Created in 11:32 上午 2020/9/2
  5. * @Version: 1.0
  6. * @Modified By:
  7. * @Description:
  8. */
  9. import java.io.DataInputStream;
  10. import java.io.FileInputStream;
  11. public class Decorator {
  12. public static void main(String[] args) throws Exception{
  13. //说明
  14. //1. InputStream 是抽象类, 类似我们前面讲的 Drink
  15. //2. FileInputStream 是 InputStream 子类,类似我们前面的 DeCaf, LongBlack
  16. //3. FilterInputStream 是 InputStream 子类:类似我们前面 的 Decorator 修饰者
  17. //4. DataInputStream 是 FilterInputStream 子类,具体的修饰者,类似前面的 Milk, Soy 等
  18. //5. FilterInputStream 类 有 protected volatile InputStream in; 即含被装饰者
  19. //6. 分析得出在 jdk 的 io 体系中,就是使用装饰者模式
  20. DataInputStream dis = new DataInputStream(new FileInputStream("d:\\abc.txt"));
  21. System.out.println(dis.read());
  22. dis.close();
  23. }
  24. }

 六、装饰模式的应用

6.1 装饰者模式何时使用

  • 在不想增加很多子类的情况下扩展类时

6.2 装饰者模式方法

  • 将具体功能职责划分,同时继承装饰者模式

5.3 装饰者模式优点

  • 装饰类和被装饰类可以独立发展,而不会相互耦合。它有效地把类的核心职责和装饰功能分开了
  • 装饰模式是继承关系的一个替代方案
  • 装饰模式可以动态地扩展一个实现类的功能

6.4 装饰者模式缺点

  • 多层装饰比较复杂。比如我们现在有很多层装饰,出了问题,一层一层检查,最后发现是最里层的装饰出问题了,想想工作量都害怕

6.5 装饰者模式使用场景

  • 需要扩展一个类的功能时
  • 需要动态地给一个对象增加功能,并可以动态地撤销时
  • 需要为一批的兄弟类进行改装或加装功能时

6.6 装饰者模式应用实例

  • 旧机包装成新机,手机/电脑内部配件不变,只是换个外壳
  • 换衣小游戏,人还是那个人,不断给她换衣服,还可以一层套一层的
  • 孙悟空有72变,变成什么后就有了它的功能,但本质还是一只猴子

七、大白话小结

继承可实现功能的扩展,但如果需要拓展的功能的种类很繁多(KFC肯得基订单需求),那么势必生成很多子类。装饰者模式通过:装饰者类内部含有被装饰者(组合关系),且被装饰者与装饰者都继承自共同的父类。这样可以通过将被装饰者的子类实例对象  传入-> 装饰者子类的实例对象中,拓展被装饰者继承类即可实现动态的将新功能 附加到装饰者子类实例对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp)。

 


文章最后,给大家推荐一些受欢迎的技术博客链接:

  1. JAVA相关的深度技术博客链接
  2. Flink 相关技术博客链接
  3. Spark 核心技术链接
  4. 设计模式 —— 深度技术博客链接
  5. 机器学习 —— 深度技术博客链接
  6. Hadoop相关技术博客链接
  7. 超全干货--Flink思维导图,花了3周左右编写、校对
  8. 深入JAVA 的JVM核心原理解决线上各种故障【附案例】
  9. 请谈谈你对volatile的理解?--最近小李子与面试官的一场“硬核较量”
  10. 聊聊RPC通信,经常被问到的一道面试题。源码+笔记,包懂
  11. 深入聊聊Java 垃圾回收机制【附原理图及调优方法】

欢迎扫描下方的二维码或 搜索 公众号“大数据高级架构师”,我们会有更多、且及时的资料推送给您,欢迎多多交流!

                                           

       

 

注:本文转载自blog.csdn.net的不埋雷的探长的文章"https://blog.csdn.net/weixin_32265569/article/details/108355392"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top