23种设计模式大概分为三大类:
5种(创建型模式):工厂方法模式、抽象工厂模式、单例模式、原型模式、建造者模式。
7种(结构型模式):适配器模式、桥接模式、代理模式、外观模式、装饰者模式、组合模式、享元模式。
11种(行为型模式):策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
行为型又可以通过类与类之间的关系进行划分 :
装饰者模式基本介绍:
- 装饰者模式(Decorator):动态地将新功能附加到对象上,在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp)
-
装饰者模式就像打包一个快递
主体:比如:陶瓷、衣服 (Component) //被装饰者
包装:比如:报纸填充、塑料泡沫、纸板、木板(Decorator) //装饰者
-
Component: 主体
-
ConcreteComponent 和 Decorator
-
ConcreteComponent:具体的主体
-
Decorator:装饰者,比如各调料,装饰者里聚合了一个Component,也就是说,ConcreteComponent是可以放到装饰者里面的(装饰者里面可以包含被装饰者)
-
-
在如图的 Component 与 ConcreteComponent 之间,如果 ConcreteComponent 类很多,还可以设计一个缓冲层,将共有的部分提取出来,抽象成一个类。
-
星巴克咖啡馆
-
咖啡种类/单品咖啡:Espresso(意大利浓咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(无因咖啡)
-
调料:Milk、Soy(豆浆)、Chocolate
要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便。
要求计算不同种类咖啡的费用: 客户可以点单品咖啡,也可以单品咖啡+调料组合。
2. 传统方案思路一分析 - 类图
类图分析:
-
Drink 是一个抽象类,表示饮料
- description 就是对咖啡的描述,比如咖啡的名字
- cost()方法就是计算费用,Drink 类中做成一个抽象方法
-
Decaf 就是单品咖啡,继承 Drink,并实现了 cost()
-
Espress && Milk 就是单品咖啡+调料, 这个组合很多
方案一问题分析:这样设计会有很多类,当需要增加一个单品咖啡,或者一个新的调料时,类的数量就会倍增,就会出现类爆炸
思路二分析
前面思路一因为咖啡单品+调料组合会造成类的倍增,因此可以做改进,将调料内置到 Drink 类,这样就不会造成类数量过多,从而提高项目的维护性
说明:milk,soy,chocolate 可以设计为 Boolean,表示是否要添加相应的调料
方案二问题分析:
- 方案 2 可以控制类的数量,不至于造成很多的类
- 在增加或者删除调料种类时,代码的维护量很大
- 考虑到用户可以添加多份调料,可以将 hasMilk 返回一个对应 int
- 改进:考虑使用 装饰者 模式
使用装饰者模式改进传统方式,让程序具有更好的扩展性
思路分析 - 类图
装饰者模式下的订单:2份巧克力 + 一份牛奶的LongBlack
说明:
- Milk包含了LongBlack
- 一份Chocolate包含了(Milk+LongBlack)
- 一份Chocolate包含了(Chocolate+Milk+LongBlack)
- 这样,不管是什么形式的单品咖啡+调料组合,通过递归方式都可以方便地组合和维护
具体实现
// 接口 Drink.java
public abstract class Drink {
public String des;//描述
private float price = 0.0f;
// get / set方法略
//计算费用的抽象方法:由子类实现
public abstract float cost();
}
// Coffee.java
public class Coffee extends Drink{
@Override
public float cost() {
return super.getPrice();
}
}
// Espresso.java/LongBlack.java/ShortBlack.java
public class Espresso extends Coffee{
public Espresso() {
setDes("意大利咖啡");
setPrice(6.0f);
}
}
// Decorator.java 继承Drink又组合Drink
public class Decorator extends Drink{
private Drink drink;
public Decorator(Drink drink) {
this.drink = drink;
}
@Override
public float cost() {
//自己(调料)的价格+咖啡的价格
return super.getPrice() + drink.getPrice();
}
@Override
public String getDes() {
//父类的信息+被装饰者的信息
return super.des+" "+super.getPrice()+" && "+drink.getDes();
}
}
// Chocolate.java/Milk.java/Soy.java
//具体的Decorator,这里就是调味品
public class Chocolate extends Decorator {
public Chocolate(Drink drink) {//必须有构造器
super(drink);
setDes("巧克力");
setPrice(3.5f);
}
}
// CoffeeBar.java 使用
public class CoffeeBar {
public static void main(String[] args) {
//用装饰者模式下单:2份Chocolate+1份milk+LongBlack
//1.先点一份LongBlack
Drink coffee = new LongBlack();
System.out.println("单品:"+coffee.getDes()+" 价格:"+coffee.cost());
//2.加入一份牛奶
coffee = new Milk(coffee);
System.out.println("加入一份牛奶后:"+coffee.getDes()+" 价格:"+coffee.cost());
//3.加入一份巧克力
coffee = new Chocolate(coffee);
System.out.println("加入一份牛奶+巧克力后:"+coffee.getDes()+" 价格:"+coffee.cost());
//4.再加入一份巧克力
coffee = new Chocolate(coffee);
System.out.println("加入一份牛奶+2份巧克力后:"+coffee.getDes()+" 价格:"+coffee.cost());
}
}