经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » 设计模式 » 查看文章
java设计模式-装饰者模式详解
来源:jb51  时间:2021/7/21 12:33:53  对本文有异议

引例

需求:设现在有单品咖啡:Espresso(意大利浓咖啡)和LongBlack(美式咖啡),调料有Milk(牛奶)和sugar(糖),客户可以点单品咖啡或单品咖啡+调料的组合,计算相应费用。要求在扩展新的咖啡种类时,具有良好的扩展性、改动维护方便。

抛砖引玉,我们先看看两种一般解法。

一般解法

方案一、

枚举创建每一种组合可能,Drink抽象类表示饮料,cost()方法计算价格,子类如Longblack_Milk表示美式咖啡加牛奶:

在这里插入图片描述

这样设计十分不明智,会有很多类,当新增一个单品咖啡或调料时,类的数量就会倍增,出现类爆炸。

方案二、

把调料内置到Drink类,减少类数量过多:

在这里插入图片描述

方案二虽然不至于造成很多类,但是增加或删除调料时,代码维护量仍旧很大。

装饰者模式

装饰者模式(Decorator Pattern)是结构型模式,也称装饰器模式/修饰模式。它可以动态的将新功能附加到对象上,同时又不改变其结构。在对象功能扩展方面,它比继承更有弹性。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

类图:

在这里插入图片描述

  1. Component抽象类:主体,比如类似前面的Drink。
  2. ConcreteComponent类:具体的主体,比如前面的单品咖啡。
  3. Decorator类:装饰者,比如前面的调料
  4. ConcreteDecorator类:具体的装饰者,比如前面的牛奶。

如果ConcreteComponent具体子类很多,那么可以再加一个中间层,提取共同部分,通过继承实现更多不同的具体子类。

装饰者解法

类图:

在这里插入图片描述

Drink 类就是前面说的抽象类Decorator 是一个装饰类,含有一个被装饰的对象(Drink obj)和的cost()方法进行一个费用的叠加计算,递归的计算价格Milk和Suger是具体的装饰者Coffee是被装饰者主体LongBlack和Espresso是具体实现的被装饰者实体

代码:

抽象类

  1. public abstract class Drink {//抽象类
  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. public abstract float cost();
  18. }

装饰者

  1. public class Decorator extends Drink {//装饰者
  2. private Drink obj;
  3. public Decorator(Drink obj) { //组合
  4. this.obj = obj;
  5. }
  6. @Override
  7. public float cost() {
  8. // getPrice 自己价格
  9. return super.getPrice() + obj.cost();
  10. }
  11. @Override
  12. public String getDes() {
  13. // obj.getDes() 输出被装饰者的信息
  14. return des + " " + getPrice() + " && " + obj.getDes();
  15. }
  16. }
  17. public class Milk extends Decorator {//装饰者子类
  18. public Milk(Drink obj) {
  19. super(obj);
  20. setDes(" 牛奶 ");
  21. setPrice(2.0f);
  22. }
  23. }
  24. public class Suger extends Decorator {//装饰者子类
  25. public Suger(Drink obj) {
  26. super(obj);
  27. setDes(" 糖 ");
  28. setPrice(1.0f);
  29. }
  30. }

被装饰者

  1. public class Coffee extends Drink {//被装饰者
  2. @Override
  3. public float cost() {
  4. return super.getPrice();
  5. }
  6. }
  7. public class Espresso extends Coffee {//被装饰者子类
  8. public Espresso() {
  9. setDes(" 意式咖啡 ");
  10. setPrice(6.0f);
  11. }
  12. }
  13. public class LongBlack extends Coffee {//被装饰者子类
  14. public LongBlack() {
  15. setDes(" 美式咖啡 ");
  16. setPrice(5.0f);
  17. }
  18. }

客户端测试

  1. public class Client {
  2. public static void main(String[] args) {
  3. // 阿姨的卡布奇诺:意式加两份牛奶、一份糖
  4. // 1. 点一份Espresso
  5. Drink order = new Espresso();
  6. System.out.println("order1 费用=" + order.cost());
  7. System.out.println("order1 描述=" + order.getDes());
  8. // 2.1 order 加一份牛奶
  9. order = new Milk(order);
  10. System.out.println("order 加入一份牛奶 费用 =" + order.cost());
  11. System.out.println("order 加入一份牛奶 描述 = " + order.getDes());
  12. // 2.2 order 再加一份牛奶
  13. order = new Milk(order);
  14. System.out.println("order 加入两份牛奶 费用 =" + order.cost());
  15. System.out.println("order 加入两份牛奶 描述 = " + order.getDes());
  16. // 3. order 加一份糖
  17. order = new Suger(order);
  18. System.out.println("order 两份牛奶、一份糖 费用 =" + order.cost());
  19. System.out.println("order 两份牛奶、一份糖 描述 = " + order.getDes());
  20. System.out.println("===========================");
  21. //美式咖啡加一份牛奶
  22. //1. 点一份LongBlack
  23. Drink order2 = new LongBlack();
  24. System.out.println("order2 费用 =" + order2.cost());
  25. System.out.println("order2 描述 = " + order2.getDes());
  26. //2. order2 加一份牛奶
  27. order2 = new Milk(order2);
  28. System.out.println("order2 加入一份牛奶 费用 =" + order2.cost());
  29. System.out.println("order2 加入一份牛奶 描述 = " + order2.getDes());
  30. }
  31. }

运行结果:

在这里插入图片描述

总结:

装饰者模式就像打包一个快递,不断的动态添加新的功能,可以组合出所有情况:

在这里插入图片描述

第一份Milk包含一份Espresso

第二份Milk包含(Milk+Espresso)

Suger包含(Milk+Milk+Espresso)

本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注w3xue的更多内容!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号