1.概述
装饰者模式是一种结构性设计模式,它能够动态地将新功能附加到对象上,却又不改变对象,在对象功能扩展方面,比继承更富有弹性,且符合开闭原则。这种模式创建了一个类,用来包装原有的类,在保证原来类的功能前提下,增加了额外的功能。本文将分析装饰者模式的概念及使用。
2.概念及使用
2.1 类图
装饰者类图如下:
上述类图包含了装饰着四个核心的部分:抽象组件(Component)、被装饰者(ConcreteComponent)、装饰者组件(Decorator)、具体装饰类(ConcreteDecorator),具体解释如下:
(1)抽象组件(Component):抽象出一个接口,规范需要准备接受附加责任的对象;
(2)被装饰者(ConcreteComponent):需要装饰的具体对象;
(3)装饰者组件(Decorator):装饰者基类,用于持有组件对象的实例引用,装饰具体的组件对象;
(4)具体装饰(ConcreteDecorator):负责给构件对象装饰附加的功能。
2.2 案例
顾客在奶茶店里点奶茶,奶茶有不同的品种:原味奶茶、黑糖珍珠奶茶、芒果耶奶奶茶等,店员也可以根据客户需要制作不同的奶茶。这里就可以用装饰者模式来对奶茶对象进行加工,生成顾客最终需要的奶茶。
编码如下:
//定义抽象组件(奶茶)
public interface MilkyTea {
String makeTea();
Double calCost();
}
//定义被装饰者(这里以原味奶茶为基础)
public class PureMilkyTea implements MilkyTea {
private Double cost;
public Double getCost() {
return cost;
}
public void setCost(Double cost) {
this.cost = cost;
}
public String makeTea() {
System.out.println("原味奶茶基础材料");
return "pureMilkyTea";
}
public Double calCost() {
System.out.println("原味奶茶基础价格为10元");
setCost(10.0);
return this.cost;
}
}
//定义装饰者组件
public abstract class Decorator implements MilkyTea{
public MilkyTea milkyTea;
public Decorator(MilkyTea milkyTea) {
this.milkyTea = milkyTea;
}
abstract void addMilk();
abstract void addMongo();
abstract void addPearl();
}
//定义具体装饰类:芒果椰奶奶茶装饰类(MongoCocoTea)和黑糖珍珠奶茶装饰类(PearlyTea)
public class MongoCocoTea extends Decorator {
public MongoCocoTea(MilkyTea milkyTea) {
super(milkyTea);
}
@Override
void addMilk() {
System.out.println("添加椰奶。。。");
}
@Override
void addMongo() {
System.out.println("添加芒果。。。");
}
@Override
void addPearl() {
System.out.println("添加珍珠。。。");
}
@Override
public String makeTea() {
System.out.println("制作芒果椰奶奶茶。。。");
milkyTea.makeTea();
addMilk();
addMongo();
System.out.println("芒果椰奶奶茶制作完成");
return "MongoCocoTea";
}
@Override
public Double calCost() {
//原味奶茶价格
Double price = milkyTea.calCost();
//加椰奶价格
price += 10;
//加芒果价格
price += 5;
return price;
}
}
public class PearlyTea extends Decorator {
public PearlyTea(MilkyTea milkyTea) {
super(milkyTea);
}
@Override
void addMilk() {
System.out.println("添加牛奶。。。");
}
@Override
void addMongo() {
System.out.println("添加芒果。。。");
}
@Override
void addPearl() {
System.out.println("添加黑糖珍珠。。。");
}
@Override
public String makeTea() {
System.out.println("制作黑糖珍珠奶奶茶。。。");
milkyTea.makeTea();
addMilk();
addPearl();
System.out.println("黑糖珍珠奶茶制作完成");
return "PearlyTea";
}
@Override
public Double calCost() {
//原味奶茶价格
Double price = milkyTea.calCost();
//加牛奶价格
price += 5;
//加黑糖珍珠价格
price += 8;
return price;
}
}
//测试代码如下
public class Test {
public static void main(String[] args) {
PureMilkyTea pureMilkyTea = new PureMilkyTea();
PearlyTea pearlyTea = new PearlyTea(pureMilkyTea);
pearlyTea.makeTea();
System.out.println("黑糖珍珠奶茶价格为" + pearlyTea.calCost());
System.out.println("-------------------------------");
MongoCocoTea mongoCocoTea = new MongoCocoTea(pureMilkyTea);
mongoCocoTea.makeTea();
System.out.println("芒果耶耶奶茶价格为:" + mongoCocoTea.calCost());
}
}
运行结果如下:
2.3 装饰者模式在jdk源码中的应用
在JAVA的IO类中,体现了装饰者模式的应用,具体类图如下:
public abstract class InputStream implements Closeable{}是一个抽象组件(Component);
public class FilterInputStream extends InputStream {}是一个装饰者类;
protected volatile InputStream in;这是具体的被装饰对象
public class DataInputStream extends FilterInputStream implements DataInput {} 是FilterInputStream的一个子类,也继承了被装饰对象InputStream。
2.4 装饰者优缺点
2.4.1 优点
1.装饰者模式提供了比继承更加符合开闭原则,具有更好的灵活性;因为继承是在静态期间确立关系,而装饰者是在运行时动态添加对象的功能;
2.解耦,通过增加子类方式来修改对象创建行为,这种通过增加而不是修改类的方式,是面向对象设计所推崇的。
2.4.2 缺点
1.会在系统中产生较多的类,造成系统臃肿;
2.使用难度较高,对设计人员抽象能力要求较高。
3.小结
1.装饰者模式一种结构性模式,它的作用是避免使用继承的方式来扩展对象的功能,而是通过动态添加的方式来增加功能;
2.装饰模式在使用时,需要根据场景进行抽象,选出关键的Component、ConcreteComponent、Decorator、ConcreteDecorator。
4.参考文献
1.《设计模式-可复用面向对象软件的基础》-Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides
2.《可复用物联网Web3D框架的设计与实现》-程亮(知网)
3.https://www.bilibili.com/video/BV1G4411c7N4-尚硅谷设计模式
4.《大话设计模式》-程杰著