装饰者模式,实现动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,其体现了开闭原则。这么说,比较抽象,我们通过一个项目需求也体会装饰者模式的好处。
1、咖啡订单项目
具体需求如下:
咖啡种类有多种:Espresso(意大利咖啡),LongBlack(美式咖啡),Decaf(无因咖啡)
调料:Milk,Chocolate
要求在扩展新的咖啡种类时,具有良好的扩展性,改动方便
计算不同种类咖啡的费用:客户可以点单品咖啡,也可以点单品咖啡+调料组合
2、传统方案1
一套继承解决,类图如下:
把Drink做成一个抽象类,表示饮料,然后各个咖啡种类与调料的组合去继承实现,非常简单暴力。带来的问题也很明显,当增加一个咖啡种类或新的调料时,类的数量就会倍增,出现类爆炸。
3、传统方式2
考虑到咖啡种类+调料会造成类爆炸,可以将调料内置到Drink类中,通过选项控制有无调料,这样就不会造成类数量过多。
好处是可以控制类的数量,但是在增加或者删除调料种类时,代码维护量很大。下面我们引入装饰者模式。
4、装饰者模式
装饰者模式就像打包一个快速
主体:比如,陶瓷、衣服等称为被装饰者
包装:比如报纸填充,塑料泡沫等称为装饰器
其角色主要分为三类:
Component主体:多为抽象类或接口,需要装饰器和被装饰者来实现
ConcreteComponent:主体的一个具体实现
Decorator:装饰器,同样要实现主体,同时要聚合主体
具体原理如下:
下面来实现具体代码
Drink即为主体,其中Coffee是对子类公共部分的的进一步封装:
public abstract class Drink {
private 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();
}
class Coffee extends Drink{
@Override
public float cost() {
return super.getPrice();
}
}
被装饰者实现主体
class Decaf extends Coffee{
public Decaf(){
setDes("无因咖啡");
setPrice(1.0f);
}
}
装饰器继承主体并聚合
public class Decorator extends Drink {
//组合
private Drink drink;
public Decorator(Drink drink){
this.drink=drink;
}
@Override
public float cost() {
//返回调料的价格和咖啡的价格
return getPrice()+drink.getPrice();
}
@Override
public String getDes() {
return super.getDes()+drink.getDes();
}
}
装饰器的一个具体实现
class Milk extends Decorator{
public Milk(Drink drink) {
super(drink);
setDes("牛奶");
setPrice(2.0f);
}
}
客户端
public class Client {
public static void main(String[] args) {
Drink decaf = new Decaf();
System.out.println(decaf.getDes());
System.out.println(decaf.getPrice());
//加点牛奶
decaf=new Milk(decaf);
System.out.println(decaf.getDes());
System.out.println(decaf.getPrice());
}
}
5、总结
装饰者模式与继承的目的都是扩展对象的功能,但是装饰者可以提供比继承更多的灵活性。以下情况可以考虑使用装饰者模式:
扩展类的功能或给一个类添加职责
需要动态的给一个对象添加功能,这些功能可以动态的撤销
需要增加一些由基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实
当不能采用子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类爆炸性增长;另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类
推荐阅读
设计模式七大原则
UML类图的六大关系
八种单例模式分析
由浅入深工厂模式
原型模式及深浅拷贝详解
建造者模式
适配器设计模式
桥接模式
网友评论