设计模式的重要性对于程序员来说,相当于盾牌对于美国队长,暴风战斧相对于雷神,内裤对于绿巨人(绿巨人最强武器,手动狗头)来说,是必不可少的。
在此,特别总结下23钟设计模式:
- 创建型模式:单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式。
- 结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
- 行为型模式:模版方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、 解释器模式(Interpreter 模式)、状态模式、策略模式、职责链模式(责任链模式)。
本人将会以专栏形式详细介绍下每种设计模式,文章代码使用java语言,希望能够帮助读者打造一柄编码前行路上的神兵利器。
本文主要针对设计模式的第六种,装饰者模式进行详细介绍
1.首先看一个需求订购奶茶
- 奶茶的种类:焦糖奶茶,鲜芋奶茶,港式奶茶等
- 配料:红豆,珍珠,椰果等
- 要求在增加新的奶茶种类和新的配料时候有良好的拓展性
- 计算不同奶茶的费用:可以单点,也可以添加多种配料
2.对于需求的初步分析,给出方案一
3. 方案一分析
- MilkTea是一个抽象类,表示奶茶
- description为描述
- cost()计算价格,是MilkTea的一个抽象方法
- GangshiMikeTea(港式),JiaotangMikeTea(焦糖),XianyuMikeTea(鲜芋)为单品奶茶
- JiaotangMikeTea&&Zhenzhu,GangshiMikeTea&&RedBean为单品加配料,这个组合有很多种
问题:这样设计,会有很多类,当我们增加一个单品咖啡,或者一个新的调料,类的数量就会倍增,就会出现类爆炸
4.方案二解决奶茶问题(方案一的改进)
前面分析到方案 一 因为奶茶单品+配料组合会造成类的倍增,因此可以做改进,将配料内置到 MilkTea 类,这样就不会造成类数量过多。从而提高项目的维护性(如图)
说明: redBean,coconut,zhenzhu可以设计为 Boolean,表示是否要添加相应的配料
5. 方案二的分析
- 方案二可以控制类的数量,不至于造成很多的类
- 在增加配料时候,涉及到的改动,代码维护量很大
- 用户需要多份配料可以更改hasXXX方法放回int表示份数
- 违反了开闭原则
- 考虑使用装饰者模式
6. 装饰者模式基本介绍
- 装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp)
- 这里提到的动态的将新功能附加到对象和 ocp 原则,读者可以从后续的代码演示中理解
7. 装饰者模式原理
- 装饰者模式就像打包一个快递 主体:比如:陶瓷、衣服 (Component) // 被装饰者 包装:比如:报纸填充、塑料泡沫、纸板、木板(Decorator)
- Component 主体:比如类似前面的 MilkTea
- ConcreteComponent 和 Decorator,ConcreteComponent:具体的主体,比如前面的各个单品奶茶
- Decorator: 装饰者,比如各配料
在如图的 Component 与 ConcreteComponent 之间,如果 ConcreteComponent 类很多,还可以设计一个缓冲层,将共有的部分提取出来,抽象层一个类。
8. 装饰者模式解决奶茶订单
-
装饰者模式下的订单:2 份珍珠+一份红豆的港式奶茶
说明: -
RedBeans包含了GangshiMilkTea
-
中层的Zhenzhu包含了(GangshiMilkTea+RedBeans)
-
最外层的Zhenzhu包含了(GangshiMilkTea+RedBeans+Zhenzhu)
9.装饰者模式奶茶订单项目应用实例
/**
* 奶茶
*/
public abstract class MilkTea {
public String des; // 描述
private float price = 0.0f;
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
//计算费用的抽象方法
public abstract float cost();
}
/**
* Coco奶茶
*/
public class Coco extends MilkTea {
@Override
public float cost() {
return super.getPrice();
}
}
/**
* 港式奶茶
*/
public class GangshiMilkTea extends Coco{
public GangshiMilkTea() {
setDes("港式奶茶...........");
setPrice(10.0f);
}
}
/**
* 焦糖奶茶
*/
public class JiaotangMilkTea extends Coco{
public JiaotangMilkTea() {
setDes("焦糖奶茶...........");
setPrice(12.0f);
}
}
/**
* 鲜芋奶茶
*/
public class XianyuMilkTea extends Coco{
public XianyuMilkTea() {
setDes("鲜芋奶茶...........");
setPrice(13.0f);
}
}
/**
* 装饰器
*/
public class Decorator extends MilkTea {
private MilkTea milkTea;
public Decorator(MilkTea milkTea) {
this.milkTea = milkTea;
}
@Override
public float cost() {
return super.getPrice()+milkTea.cost();
}
}
/**
* 配料红豆
*/
public class RedBeans extends Decorator{
public RedBeans(MilkTea milkTea) {
super(milkTea);
setDes("加红豆");
setPrice(2.0f);
}
}
/**
* 配料椰果
*/
public class Coconut extends Decorator{
public Coconut(MilkTea milkTea) {
super(milkTea);
setDes("加椰果");
setPrice(3.0f);
}
}
/**
* 配料珍珠
*/
public class Zhenzhu extends Decorator{
public Zhenzhu(MilkTea milkTea) {
super(milkTea);
setDes("加珍珠");
setPrice(4.0f);
}
}
/**
* 下单
*/
public class OrderClient {
public static void main(String[] args) {
//1.点一杯港式奶茶
MilkTea milkTea = new GangshiMilkTea();
System.out.println("港式奶茶的费用: "+milkTea.cost());
System.out.println("港式奶茶的描述: "+milkTea.getDes());
//加红豆
milkTea = new RedBeans(milkTea);
System.out.println("港式奶茶加红豆的费用: "+milkTea.cost());
System.out.println("港式奶茶加红豆的描述: "+milkTea.getDes());
//加椰果
milkTea = new Coconut(milkTea);
System.out.println("港式奶茶加红豆再加椰果的费用: "+milkTea.cost());
System.out.println("港式奶茶加红豆再加椰果的描述: "+milkTea.getDes());
//加珍珠
milkTea = new Zhenzhu(milkTea);
System.out.println("港式奶茶加红豆再加椰果再加珍珠的费用: "+milkTea.cost());
System.out.println("港式奶茶加红豆再加椰果再加珍珠的描述: "+milkTea.getDes());
//加珍珠
milkTea = new Zhenzhu(milkTea);
System.out.println("港式奶茶加红豆再加椰果再加2份珍珠的费用: "+milkTea.cost());
System.out.println("港式奶茶加红豆再加椰果再加2份珍珠的描述: "+milkTea.getDes());
}
}
最终的结果:
10. 装饰者模式在 JDK 应用的源码分析
Java 的 IO 结构,FilterInputStream 就是一个装饰者
上图引用自JAVA IO流结构图概览
- InputStream 是抽象类, 类似我们前面讲的 MilkTea
- FileInputStream 是 InputStream 子类,类似我们前面的 GangshiMilkTea, JiaotangMilkTea
- FilterInputStream 是 InputStream 子类:类似我们前面 的 Decorator 修饰者
- DataInputStream 是 FilterInputStream 子类,具体的修饰者,类似前面的 RedBeans, Zhenzhu等
- FilterInputStream 类 有 protected volatile InputStream in; 即含被装饰者
- 分析得出在 jdk 的 io 体系中,就是使用装饰者模式