当前位置:首页 > 技术 > 正文内容

装饰者模式

Lotus2022-12-08 13:30技术

晚风吹人醒,万事藏于心。我没说不公平,也没有说苦,我说我知道了。

欢迎来到星巴兹咖啡

Beverage是店里所有饮料的抽象类,下面是饮料的不同口味。

在日常生活中,你在购买时,可能还会加一些小料(凑单满减),例如燕奶(Steamed Milk)、豆浆(Soy)、摩卡(Mocha)等,在付款时,电脑的订单系统会根据你点的饮料和加的小料计算出总的价钱。

我们现在就是要设计一个能自动计算价格的订单系统。

最简单的方法,就是所有的饮料包括小料都写一个实现类,但是这样在后期就是一个维护噩梦。不说可能有几百种饮料几百种实现方式,如果后期稍微改动其中一款小料的价格,那么你就需要到一个一个的实现类里面去进行修改,严重违反了软件的设计原则。

tips:

代码应该如同晚霞中的莲花一样地关闭(免于改变),如同晨曦中的莲花一样地开放(能够扩展)。

设计原则:

类应该对扩展开放,对修改关闭。

这样的设计具有弹性可以应对改变,可以接受新的功能来应对改变的需求。

注意:在选择需要被扩展的代码部分时要小心。每个地方都采用开放-关闭原则,是一种浪费,也没有必要,还会导致代码变得复杂且难以理解,要找到平衡点。

认识装饰者模式

在上面星巴兹咖啡的设计中,实现类数量爆炸、设计死板、以及基类加入新功能不适用于所有的子类。

用装饰者模式进行设计

  1. 拿一个深色烘焙咖啡(DarkRoast)对象

  2. 以摩卡(Mocha)对象装饰它

  3. 以奶泡(Whip)对象装饰它

  4. 调用cost()方法,并委托(delegate)将调料的价钱加上去

简单来讲就是将对象一层一层包起来,在调用的时候,先一层一层进去,之后一层一层计算结果出来。

定义装饰者模式

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

使用装饰者模式设计星巴兹类图

注意:这里使用继承是达到“类型匹配”的目的(!!!),而不是利用继承来获得行为。

新咖啡师傅特训

如果有一张单子点的是:“双倍摩卡豆浆奶泡拿铁咖啡”,进行设计实现。

流程

核心代码实现

总抽象类,装饰类与被装饰类都实现此类,达到类型匹配

/**
* @Description 抽象类饮料
* @Author lh
* @Date 2022/12/6 19:31
*/
public abstract class Beverage {
   public String description = "Unknown Beverage";

   public String getDescription() {
       return description;
  }

   public abstract double cost();
}

被装饰类,不同口味饮料

/**
* @Description 意大利浓缩咖啡
* @Author lh
* @Date 2022/12/6 19:37
*/
public class Espresso extends Beverage {

   public Espresso() {
       description = "Espresso";
  }

   @Override
   public double cost() {
       return 1.99;
  }
}
/**
* @Description 家庭混合咖啡
* @Author lh
* @Date 2022/12/6 19:39
*/
public class HouseBlend extends Beverage {

   public HouseBlend() {
       description = "House Blend Coffee";
  }

   @Override
   public double cost() {
       return .89;
  }
}

装饰抽象类

/**
* @Description 装饰类调料抽象类
* @Author lh
* @Date 2022/12/6 19:35
*/
public abstract class CondimentDecorator extends Beverage {
   public abstract String getDescription();
}

装饰类实现

/**
* @Description 调料摩卡
* @Author lh
* @Date 2022/12/6 19:41
*/
public class Mocha extends CondimentDecorator{
   private final Beverage beverage;

   public Mocha(Beverage beverage) {
       this.beverage = beverage;
  }

   @Override
   public String getDescription() {
       return beverage.getDescription() + ", Mocha";
  }

   public double cost() {
       return .20 + beverage.cost();
  }
}
/**
* @Description 调料奶泡
* @Author lh
* @Date 2022/12/6 19:51
*/
public class Whip extends CondimentDecorator{
   private final Beverage beverage;

   public Whip(Beverage beverage) {
       this.beverage = beverage;
  }

   @Override
   public String getDescription() {
       return beverage.getDescription() + ", Whip";
  }

   public double cost() {
       return .15 + beverage.cost();
  }
}

实现 双倍摩卡豆浆奶泡拿铁咖啡

/**
* @Description 星巴兹计算
* @Author lh
* @Date 2022/12/6 20:10
*/
public class StarbuzzCoffee {
   public static void main(String[] args) {
       Beverage beverage = new HouseBlend();
       System.out.println(beverage.getDescription() + ":" + beverage.cost() + "元");

       Beverage beverage1 = new HouseBlend();
       beverage1 = new Mocha(beverage1);
       beverage1 = new Mocha(beverage1);
       beverage1 = new Whip(beverage1);
       System.out.println(beverage1.getDescription() + ":" + beverage1.cost() + "元");
  }
}

真实世界的装饰者:Java I/O

下面是一个典型的对象集合,用装饰者来将功能结合起来,以读取文件数据。

和星巴兹的设计相比,java.io其实并没有多大的差距。

核心代码示例

/**
* @Description 获取文本行数
* @Author lh
* @Date 2022/12/7 19:34
*/
public class LowerNumberInputStream extends FilterInputStream {

   public LowerNumberInputStream(InputStream in) {
       super(in);
  }

   public int read() throws IOException {
       int c = super.read();
       return (c == -1 ? c : Character.toLowerCase(c));
  }

   public int read(byte[] b, int offset, int len) throws IOException {
       int result = super.read(b, offset, len);
       for (int i = offset; i < offset + result; i++) {
           b[i] = (byte) Character.toLowerCase(b[i]);
      }
       return result;
  }
}
/**
* @Description IO测试
* @Author lh
* @Date 2022/12/7 19:40
*/
public class InputTest {
   public static void main(String[] args) throws FileNotFoundException {
       int c;
       try {
           InputStream in = new LowerNumberInputStream(new BufferedInputStream(new FileInputStream("text.txt")));
           while ((c = in.read()) >= 0) {
               System.out.println(c);
          }
      } catch (Exception e) {
           e.printStackTrace();
      }
  }
}

总结

  • 装饰者和被装饰者对象有相同的超类型(类型匹配)。

  • 你可以用一个或多个装饰者包装一个对象。

  • 既然装饰者和被装饰者有相同的超类型,所以在任何需要原始对象(被包装的)场合,可以用装饰过的对象代替它。

  • 装饰者可以在所委托被装饰者的行为之前与之后,加上自己的行为,以达到特定的目的。

  • 对象可以在任何时候被装饰,所以可以在运行时动态地、不限量得使用你喜欢的装饰者来装饰对象。

代码地址

https://gitee.com/LHDAXIE/design-mode

原文链接

扫描二维码推送至手机访问。

版权声明:本文来源于网络,仅供学习,如侵权请联系站长删除。

本文链接:https://news.layui.org.cn/post/1137.html

分享给朋友:

“装饰者模式” 的相关文章

[s905l3]性价比神机mgv3000全网首拆,刷armbian实现更多价值!

最近花55淘了一台mgv3000,s905l3,2+16G带蓝牙,真的性价比没得说 S905L3 工艺28nm差于s905l3a 主频1.9Ghz,超频可以达到2Ghz,GPU是Mail450,当服务器跑ha,nas什么都是很不错的。 而且还自带蓝牙,总体性价比比s905l3a系列高多了 按我的方法可以启动,网卡没有问题,但是目前没有显示,没有蓝牙。 等之后我有时间了照着安卓的dtb改一改也许会解...

【微信小程序】认识小程序页面

????系列专栏:微信小程序 ????欢迎关注????点赞????收藏⭐留言???? ✅个人主页:​​hacker_demo的51CTO博客​​ ????个人格言:不断的翻越一座又一座的高山,那样的人生才是我想要的。这一马平川,一眼见底的活,我不想要,我的人生,我自己书写,余生很长,请多关照,我的人生,敬请期待???????????? 新建小程序页面 只需要在app.json->...

Python基础(十) | Numpy详细教程

第十一章 Numpy库 ⭐本专栏旨在对Python的基础语法进行详解,精炼地总结语法中的重点,详解难点,面向零基础及入门的学习者,通过专栏的学习可以熟练掌握python编程,同时为后续的数据分析,机器学习及深度学习的代码能力打下坚实的基础。 ????本文已收录于Python基础系列专栏: Python基础系列教程 欢迎订阅,持续更新。 10.1 为什么要用Numpy 10.1.1  ...

前端三剑客快速入门(二)

前言 本文书接上回,继续css的知识,序号就重新开始了。上篇内容:前端三剑客快速入门(一) CSS 盒子模型 盒子模型属性: border外框 margin外边距 padding内边距 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <me...

【大话云原生】煮饺子与docker、kubernetes之间的关系

文章开始之前,我给大家推荐一个人工智能学习网站,首先说我之前是完全不涉及人工智能领域的,但是我尽然看懂了,以后老哥我就要参与人工智能了。如果你也想学习,点击跳转到网站 云原生的概念最近非常火爆,企业落地云原生的愿望也越发强烈。看过很多关于云原生的文章,要么云山雾罩,要么曲高和寡。 所以笔者就有了写《大话云原生》系列文章的想法,期望用最通俗、简单的语言说明白云原生生态系统内的组成及应用关系。那么,...

虚拟线程 - VirtualThread源码透视

前提 JDK19于2022-09-20发布GA版本,该版本提供了虚拟线程的预览功能。下载JDK19之后翻看了一下有关虚拟线程的一些源码,跟早些时候的Loom项目构建版本基本并没有很大出入,也跟第三方JDK如鹅厂的Kona虚拟线程实现方式基本一致,这里分析一下虚拟线程设计与源码实现。 Platform Thread与Virtual Thread 因为引入了虚拟线程,原来JDK存在java.lang....

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。