一、基本介绍
装饰器模式(结构型):在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,即对核心功能进行拓展。
二、包含角色
1.抽象构件角色:定义一个抽象接口以规范准备接收附加责任的对象。
2.具体构件角色:实现抽象构件,该角色提供核心功能,被包装的角色。
3.抽象装饰角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
4.具体装饰角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。
三、案例及UML类图
案例说明:
煎饼主要分为山东的杂粮煎饼和天津的煎饼果子,煎饼都有name和生产的方法,当我们吃煎饼的时候总是需要加一些辅料(即扩增功能),如鸡蛋,火腿,肉松等,但是我们不确定客户会加一样或者多样,但归根到底都是在其煎饼上加(也就是为核心功能进行扩增),如果使用继承的方式,需要为煎饼类的子类山东煎饼或天津煎饼创建更多的子类,如鸡蛋山东煎饼、鸡蛋天津煎饼、鸡蛋火腿山东煎饼等等,如果再新增一种辅料或一个其它类型的煎饼,则要扩增更多的子类,会产生类爆炸的问题。
UML类图:
类Pancake:
public abstract class Pancake {
protected String name;
/**
* 做出一张煎饼
*/
public abstract void product();
}
说明:煎饼抽象类,抽象构件角色,定义煎饼的属性和功能。
类ShandongPancake:
public class ShandongPancake extends Pancake {
public ShandongPancake() {
name = "山东煎饼";
}
@Override
public void product() {
System.out.println("正在做:"+name);
}
}
说明:山东煎饼类,具体构件角色,生产山东煎饼。
类TianjinPancake:
public class TianjinPancake extends Pancake {
public TianjinPancake() {
name = "天津煎饼";
}
@Override
public void product() {
System.out.println("正在做:"+name);
}
}
说明:天津煎饼类,具体构件角色,生产天津煎饼。
类PancakeAddIngredients:
public abstract class PancakeAddIngredients extends Pancake {
private Pancake pancake;
public PancakeAddIngredients(Pancake pancake) {
this.pancake = pancake;
}
@Override
public void product() {
pancake.product();
}
}
说明:辅料抽象类,抽象装饰角色,定义辅料的功能和方法,并调用煎饼的核心方法。
类EggAddIngredients:
public class EggAddIngredients extends PancakeAddIngredients {
public EggAddIngredients(Pancake pancake) {
super(pancake);
}
@Override
public void product() {
super.product();
addEgg();
}
public void addEgg() {
System.out.println("添加鸡蛋");
}
}
说明:鸡蛋辅料类,具体装饰角色,对煎饼进行鸡蛋的添加。
类HamAddIngredients:
public class HamAddIngredients extends PancakeAddIngredients {
public HamAddIngredients(Pancake pancake) {
super(pancake);
}
@Override
public void product() {
super.product();
addHam();
}
public void addHam() {
System.out.println("添加火腿");
}
}
说明:火腿辅料类,具体装饰角色,为煎饼进行火腿的添加。
类DecoratorTest:
public class DecoratorTest {
public static void main(String[] args) {
//吃一个山东煎饼
Pancake pancake = new ShandongPancake();
//把煎饼放入鸡蛋装饰着中加鸡蛋
Pancake egg = new EggAddIngredients(pancake);
//做一个山东鸡蛋煎饼
egg.product();
//吃一个天津煎饼
pancake = new TianjinPancake();
//把煎饼放入鸡蛋装饰着中加鸡蛋
egg = new EggAddIngredients(pancake);
//把加了鸡蛋的煎饼放入火腿装饰着中加火腿
Pancake ham = new HamAddIngredients(egg);
//做一个天津鸡蛋火腿煎饼
//注意此时已经为两次装饰
ham.product();
}
}
说明:测试及客户端类。
四、适用场景
1.当需要给一个现有类添加附加职责或功能,而又不能采用生成子类的方法进行扩充时。
2.当对象的拓展功能要求可以动态地添加,也可以再动态地撤销时。
3.当需要通过对现有的一组基本功能进行扩增,且这些功能不确定叠加情况时使用装饰者能很好解决。
五、其它
注意:在实际开发中,抽象装饰角色很多情况下都是没有的,例如IO模块和mybatis的缓存模块中都不存在抽象装饰角色,抽象装饰角色在一定程度上增加了代码的复杂度。