装饰器模式
基本介绍
装饰器模式意图为一个对象扩展新的功能,且不改变原有的结构,装饰器模式属于结构型设计模式
一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀
使用场景
- 扩展一个类的功能
- 动态增加功能,动态撤销
假设有一家咖啡公司,姑且咱就叫怪兽咖啡吧,该咖啡公司是以扩展速度最快而闻名的咖啡公司(像瑞幸咖啡一样),但是最近由于扩展速度太快,它们想重新设计一套订单系统,以满足它们现在的饮料需求。
他们最初始的设计如下:
然鹅用户在购买咖啡时,通常会添加一些调料,例如:摩卡、豆浆、奶泡等等。怪兽咖啡并根据业务去计算相应的总价,这就要求咖啡公司在设计订单时需要考虑这些调料的部分。然后就设计出这么一套庞杂的系统,因为奶茶种类太多,调料太多。种类*调料 = 类爆炸,实在太疯狂了
这么设计的问题显而易见
- 调料价格变化时,需要更改现有代码
- 出现新的调料,需要添加新的方法,并改动超类中的
cost()
方法 - 以后开发新的饮料,对于这些饮料,某些调料并不适合,但是这个设计中,子类仍需继承那些不需要的方法
装饰器模式
从上述的设计方案来看,这显然并不是一个聪明的结果,因为会遇到 类爆炸、设计过于冗余,以及基类加入的新功能并不一定适用于所有子类
所以我们要考虑换一种方式,设想一下 我们能不能 以饮料为红花,用调料作为 绿叶 装饰它
比如,客户想要 摩卡和奶泡 搭配黑莓咖啡(口味独特),那么,我们怎么做呢?
- 拿黑莓咖啡(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());
}
}
IO体系中的装饰器
由图可见,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);