装饰者模式
装饰者模式的基本概念在生活中到时都是体现。比如装修房子,房子就是被装饰者,各种装修材料就是装饰者;我们每天都会打扮自己,我们自己就是一个被装饰者,各种衣服、饰品、化妆品就是装饰者;还有就是大家喜欢喝的奶茶,每一种奶茶就是被装饰者,加的小料就是装饰者。
装饰者模式的定义
动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更具有弹性的替代方案。
是一个符合开放-关闭原则的模式。
标准类图
Component:组件超类(也可以是一个接口),具体的组件(被装饰者)类和装饰者都继承它,保证为一致的类型。
ConcreteComponent:具体的组件(被装饰者)类。
Decorator:装饰者的基类,继承组件超类,每个装饰者都会包装一个组件,有一个变量保存这个包装的组件。
ConcreteDecorator:具体的装饰者,一个变量用来记录装饰者装饰的组件,也可扩展Component的状态。
几个注意的要点:
- 装饰者与被装饰者是具有相同的超类的。
- 可以用一个或多个装饰者包装一个对象。包装的方式是通过装饰者的构造方法传入被装饰的对象,得到一个被包装过的对象,由于装饰者与被装饰者是具有相同的超类的,所以这个被包装过的对象仍然能继续被包装。
- 装饰者可以在所委托被装饰者的行为之前或之后,加上自己的行为。
- 对象可以在任何时候被修饰,所以可以在运行时动态地,不限量地用你喜欢的装饰者来修饰。
实例
下面我们举一个实际的例子,通过代码来进一步理解装饰者模式的用途。
实际场景
当我们去奶茶店买奶茶时候,首先会选择奶茶品种,然后再向里面加小料,最后店员会用点餐机器打印出来结果,上面会有奶茶种类和小料,还会有总共的价格,那么在装饰者模式中又是怎样实现的呢?在装饰者模式中,不同品种的奶茶就是组件(被装饰者),小料就是装饰者,我们会通过小料来装饰奶茶,最后得到结果。
代码实现
MilkTea.java 组件超类
//奶茶,组件超类
public abstract class MilkTea {
//一个描述当前奶茶种类或小料种类的字符串
String description="MilkTea";
//返回种类的方法
public String getDescription(){
return description;
}
//一个返回当前物品价钱的抽象方法
public abstract double cost();
}
SmallMaterial.java 装饰者基类
//小料,装饰者基类继承组件超类
public abstract class SmallMaterial extends MilkTea{
//小料种类名称的返回方法
public abstract String getDescription();
}
Oreo.java 具体的组件(被装饰者)类
//奥利奥奶茶,具体的组件类
public class Oreo extends MilkTea{
//通过构造方法,传入具体奶茶的种类
public Oreo(){
description="Oreo";
}
@Override
public double cost() {
return 15;
}
}
Padding.java 具体的装饰者类
//布丁,装饰者的具体类
public class Padding extends SmallMaterial{
MilkTea milkTea; //用一个实例变量来记录被装饰的奶茶
//通过布丁的构造方法,设定它所装饰哪种奶茶
public Padding(MilkTea milkTea){
this.milkTea=milkTea;
}
//返回布丁和所装饰奶茶的总价钱
@Override
public double cost() {
return 2+milkTea.cost();
}
//返回所装饰奶茶的名称和布丁的名称
@Override
public String getDescription() {
return milkTea.getDescription()+"+padding";
}
}
Pearl.java 具体的装饰者类
//珍珠,装饰者的具体类
public class Pearl extends SmallMaterial{
MilkTea milkTea; //用一个实例变量来记录被装饰的奶茶
//通过珍珠的构造方法,设定它所装饰哪种奶茶
public Pearl(MilkTea milkTea){
this.milkTea=milkTea;
}
//返回珍珠和所装饰奶茶的总价钱
@Override
public double cost() {
return 1.5+milkTea.cost();
}
//返回所装饰奶茶的名称和珍珠的名称
@Override
public String getDescription() {
return milkTea.getDescription()+"+pearl";
}
}
使用装饰者装饰组件
public static void main(String[] args) {
MilkTea milkTea=new Oreo(); //创建一个奥利奥奶茶实例,待包装的被装饰者
milkTea=new Padding(milkTea); //使用布丁的构造方法装饰奥利奥奶茶,返回一个包装过的对象
milkTea=new Pearl(milkTea); //使用珍珠的构造方法继续装饰被包装过
//输出被装饰过对象的物品和价格信息
System.out.println(milkTea.getDescription()+" price="+milkTea.cost());
}
整个装饰过程是这样的
测试结果
显示了该被装饰者和所有装饰者的名称,总共的价钱
缺点:如果过度使用,会导致出现许多的对象,这会让程序变得很复杂。