装饰器模式

1|0基本介绍

装饰器模式意图为一个对象扩展新的功能,且不改变原有的结构,装饰器模式属于结构型设计模式

一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀

使用场景

  • 扩展一个类的功能
  • 动态增加功能,动态撤销

 

假设有一家咖啡公司,姑且咱就叫怪兽咖啡吧,该咖啡公司是以扩展速度最快而闻名的咖啡公司(像瑞幸咖啡一样),但是最近由于扩展速度太快,它们想重新设计一套订单系统,以满足它们现在的饮料需求。

他们最初始的设计如下:

 

然鹅用户在购买咖啡时,通常会添加一些调料,例如:摩卡、豆浆、奶泡等等。怪兽咖啡并根据业务去计算相应的总价,这就要求咖啡公司在设计订单时需要考虑这些调料的部分。然后就设计出这么一套庞杂的系统,因为奶茶种类太多,调料太多。种类*调料 = 类爆炸,实在太疯狂了

 

这么设计的问题显而易见

  • 调料价格变化时,需要更改现有代码
  • 出现新的调料,需要添加新的方法,并改动超类中的cost()方法
  • 以后开发新的饮料,对于这些饮料,某些调料并不适合,但是这个设计中,子类仍需继承那些不需要的方法

 

2|0装饰器模式

从上述的设计方案来看,这显然并不是一个聪明的结果,因为会遇到 类爆炸、设计过于冗余,以及基类加入的新功能并不一定适用于所有子类

 

所以我们要考虑换一种方式,设想一下 我们能不能 以饮料为红花,用调料作为 绿叶 装饰它

比如,客户想要 摩卡和奶泡 搭配黑莓咖啡(口味独特),那么,我们怎么做呢?

  • 拿黑莓咖啡(DarkRoast)作为对象
  • 用摩卡(Mocha)对象装饰它
  • 用奶泡(Whip)对象装饰它
  • 调用 cost() 方法,并通过依赖将调料价格和摩卡价格想加

 

 

上图详细的介绍了,装饰模式的总体过程

 

上图为装饰器模式的结构类图以及各个类的作用说明

 

好,下面我们详细介绍订单系统的正确设计方案

1、我们先设计饮料组件

/** * 创建一个饮料抽象类 */ public abstract class Beverage { String description = "UnKnown Beverage"; public String getDescription() { return description; } public abstract Double cost(); }

 

2、假设目前店铺中,有 综合咖啡、浓缩咖啡、黑莓

public class HouseBlend extends Beverage { public HouseBlend() { description = "综合咖啡"; } @Override public Double cost() { return 23.2; } }
public class Expresso extends Beverage{ public Expresso(){ description = "浓缩咖啡"; } @Override public Double cost() { return 19.9; } }
public class DarkRoast extends Beverage { public DarkRoast() { description = "黑莓咖啡"; } @Override public Double cost() { return 21.8; } }

 

3、下面我们实现调料类的抽象类,也就是装饰者类

/** * 调料抽象类 */ public abstract class Condiment extends Beverage { Beverage beverage; Condiment(Beverage decoratedBeverage){ this.beverage = decoratedBeverage; } public abstract String getDescription(); }

 

4、 然后我们再实现相应的 Mocha、Whip、Soy 的调料代码

/** * 摩卡 */ public class Mocha extends Condiment { public Mocha(Beverage beverage) { super(beverage); } @Override public String getDescription() { return beverage.getDescription() + "【摩卡】"; } @Override public Double cost() { return new BigDecimal(beverage.cost().toString()).add(new BigDecimal(2.2)).doubleValue(); } }
/** * 豆浆 */ public class Soy extends Condiment { public Soy(Beverage beverage) { super(beverage); } @Override public String getDescription(){ return beverage.getDescription() + "【豆浆】"; } @Override public Double cost() { return new BigDecimal(beverage.cost().toString()).add(new BigDecimal(3.6)).doubleValue(); } }
/** * 奶泡 */ public class Whip extends Condiment { public Whip(Beverage beverage){ super(beverage); } @Override public String getDescription() { return beverage.getDescription() + "【奶泡】"; } @Override public Double cost() { return new BigDecimal(beverage.cost().toString()).add(new BigDecimal(2.7)).doubleValue(); } }

 

好了,参考装饰者模式,订单模块的基本实现已经基本完成。下面是测试代码

public class Client { public static void main(String[] args) { //一杯DarkRoast 不需要调料 Beverage darkRoast = new DarkRoast(); System.out.println(darkRoast.getDescription() + "," + darkRoast.cost()); //再点一个浓缩咖啡 加 双倍摩卡 一份奶泡 Beverage beverage = new Expresso(); beverage = new Mocha(beverage); beverage = new Mocha(beverage); beverage = new Whip(beverage); System.out.println(beverage.getDescription() + "," + beverage.cost()); } }


 

3|0IO体系中的装饰器


由图可见,InputStream就是装饰者模式中的超类(Component),
ByteArrayInputStream,FileInputStream相当于 被装饰者(ConcreteComponent),这些类都提供了最基本的字节读取功能。
而另外一个和这两个类是同一级的类FilterInputStream 即为 抽象装饰者(AbstarctDecorator)
BufferedInputStream,DataInputStream,PushbackInputStream(都继承了FilterInputStream类),它们为 装饰者(Decorator),在原有基础功能上都实现了功能的扩展和增强。
 
例:用BufferedInputStream 装饰 FileInputStream,和上面Mocha(摩卡) 装饰 DarkRoast(黑莓)如出一辙

File file = new File ("hello.txt"); FileInputStream in=new FileInputStream(file); BufferedInputStream inBuffered=new BufferedInputStream (in);

__EOF__

本文作者丁可乐
本文链接https://www.cnblogs.com/dwlovelife/p/13326739.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   丁可乐  阅读(634)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示