一.定义
指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(额外功能)的模式。
二.结构
抽象构件角色:定义一个抽象接口以规范准备接收附加责任的对象。
具体构建角色:实现抽象构件,可以通过配饰角色为其添加一些职责。
抽象装饰角色:继承或实现抽象构件角色,并包含具体构件的实例,可以通过其子类扩展具体构件功能。
具体装饰角色:实现抽象装饰的相关方法,并给具体构件对象添加附加责任。
UML类图如下
三.案例
设想一下,有一家餐馆,其有炒饭等主食。可以在主食上再加鸡蛋等配菜。最后还可以算出总价。
即有一个接口,其有价格和描述两个属性,还有getter,setter方法以及获取总价cost()方法。想想看怎么实现才能够满足开闭原则,并且扩展性好。
这里可以使用装饰者模式。
UML类图
代码如下:
/**
* 抽象构件角色
*/
public abstract class Food {
//物品价格
private int price;
//物品描述
private String desc;
public Food(int price, String desc) {
this.price = price;
this.desc = desc;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
//计算总价
public abstract int cost();
}
/**
* 具体构件(炒饭)
*/
public class Rice extends Food{
public Rice() {
super(2, "米饭");
}
@Override
public int cost() {
return getPrice();
}
}
/**
* 抽象装饰角色
*/
public abstract class Decorator extends Food{
//现有食物
private Food food;
public Decorator(Food food,int price,String desc) {
super(price,desc);
this.food = food;
setDesc(food.getDesc()+desc);
}
public Food getFood() {
return food;
}
public void setFood(Food food) {
this.food = food;
}
}
/**
* 配菜(具体装饰角色)
*/
public class SideDish extends Decorator{
public SideDish(Food food,int price,String desc) {
super(food, price, desc);
}
//总钱
@Override
public int cost() {
return getFood().cost()+getPrice();
}
}
测试
public class client {
public static void main(String[] args) {
Food food = new Rice();
System.out.println(food.getDesc()+" "+food.cost()+"元");
System.out.println("--------");
food = new SideDish(food,2,"鸡蛋");
System.out.println(food.getDesc()+" "+food.cost()+"元");
System.out.println("--------");
food = new SideDish(food,3,"培根");
System.out.println(food.getDesc()+" "+food.cost()+"元");
}
}
分析:
decorator类就是装饰者,它的结构和静态代理类结构一样,继承接口也拥有接口的成员变量。这样一来,其就可以操作具体构件角色,从而对其进行扩展,但又不会改变原先对象。
装饰者模式就类似套娃(递归),一个套一个。
好处:
装饰者模式相比于继承更加灵活,有更好的扩展性,符合开闭原则。
装饰类和被装饰类可以独立发展(装饰类不会修改被装饰类),不相互耦合,装饰模式是继承的一个替代模式,其可以动态扩展一个实现类的功能。
四.使用场景
1.当不能采用继承的方式对系统进行扩充或者采用继承不利于系统的扩展和维护时。
不能采用继承有两种,一种就是像上面的例子一样,类和类的组合有很多种,如果进行组合将会产生大量的类。采用装饰者可以很好的对类进行组合并且利于扩展。
类被定义final,不能被继承。
2.不影响其他对象的情况下,动态的给单个对象添加职责。
3.对象的功能要求可以动态的进行添加撤销时。
4.类似上面案例,需要进行类嵌套来进行对象扩展。
五.装饰者模式与静态代理的区别
相同点:
结构相同,都实现类与目标类相同的接口,并且有目标类的成员变量。
都可以在不修改目标类的前提下增强目标方法。
不同点:
装饰者为了增强目标对象,静态代理为了隐藏目标对象
装饰者的目标对象是由外界传递进来,静态代理在代理类内部创建,以此来隐藏目标对象