装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
UML:
特点:
- 装饰对象和真实对象有相同的接口(Compent)。
- 装饰对象包含一个真实对象的引用(ConcreteComponent)
- 装饰对象接受所有来自客户端的请求。它把这些请求转发给真实的对象。
- 装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能
例子:
设计模型:咖啡馆点咖啡
特点:咖啡品种很多,例如有原味,摩卡,拿铁,卡布基诺等等,价格也不一样。其次,咖啡也可以根据不同的口感加不同的调料,有牛奶,糖等等。最后在结账的时候,不同的咖啡加不同的调味剂,生成的价格是不同的。
//装饰者与原型共同的接口
public abstract class Drink {
//产品描述,可能是咖啡,也可能是调味剂
public String description="";
//价格
private float price=0f;;
public void setDescription(String description){
this.description=description;
}
public String getDescription(){
return description+"-"+this.getPrice();
}
public float getPrice(){
return price;
}
public void setPrice(float price){
this.price=price;
}
//实现类具体实现价格计算
public abstract float cost();
}
//基类原型
public class Coffee extends Drink {
@Override
public float cost() {
// 获取价格
return super.getPrice();
}
}
//实现的子类脱咖啡因咖啡
public class Decaf extends Coffee {
public Decaf(){
super.setDescription("Decaf");
super.setPrice(3.0f);
}
}
public class Espresso extends Coffee{
public Espresso(){
super.setDescription("Espresso");
super.setPrice(4.0f);
}
}
//包装类,要实现原型相同的接口Drink(我们这里是抽象类)
public class Decorator extends Drink {
//引用原型
private Drink Obj;
//注入原型
public Decorator(Drink Obj){
this.Obj=Obj;
};
@Override
public float cost() {
// 它把计算价格的请求转发给真实的对象,并增加一些附加功能
return super.getPrice()+Obj.cost();
}
@Override
public String getDescription(){
return super.description+"-"+super.getPrice()+"&&"+Obj.getDescription();
}
}
//具体的装饰者 牛奶
public class Milk extends Decorator {
public Milk(Drink Obj) {
super(Obj);
super.setDescription("Milk");
super.setPrice(2.0f);
}
}
//大豆
public class Soy extends Decorator {
public Soy(Drink Obj) {
super(Obj);
super.setDescription("Soy");
super.setPrice(1.5f);
}
}
//具体的使用方式
public class CoffeeBar {
public static void main(String[] args) {
Drink order;
order=new Decaf();
System.out.println("order1 price:"+order.cost());
System.out.println("order1 desc:"+order.getDescription());
System.out.println("****************");
order=new Espresso();
order=new Milk(order);
order=new Soy(order);
order=new Milk(order);
System.out.println("order2 price:"+order.cost());
System.out.println("order2 desc:"+order.getDescription());
}
}
在设计这个模型的时候我们分析下以后可能扩展的地方有:咖啡种类,调味剂种类,这两个种类是排列组合式的搭配。
装饰者很好的体现了多用组合,少用继承,利用继承设计子类的行为,是在编译时静态决定的,而且所有的子类都会继承到相同的行为。然而,如果能够利用组合的做法扩展对象的行为,就可以在运行时动态地进行扩展。