Java设计模式笔记(8)装饰者模式
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());
}
}
运行结果如下:
制作黑糖珍珠奶奶茶。。。
原味奶茶基础材料
添加牛奶。。。
添加黑糖珍珠。。。
黑糖珍珠奶茶制作完成
原味奶茶基础价格为10元
黑糖珍珠奶茶价格为23.0
-------------------------------
制作芒果椰奶奶茶。。。
原味奶茶基础材料
添加椰奶。。。
添加芒果。。。
芒果椰奶奶茶制作完成
原味奶茶基础价格为10元
芒果耶耶奶茶价格为:25.0
2.3 装饰者模式在jdk源码中的应用
在JAVA的IO类中,体现了装饰者模式的应用,具体类图如下:
装饰器模式在 Java语言中的最著名的应用莫过于 Java I/O 标准库的设计了。例如,InputStream 的子类 FilterInputStream,OutputStream 的子类 FilterOutputStream,Reader 的子类 BufferedReader 以及 FilterReader,还有 Writer 的子类 BufferedWriter、FilterWriter 以及 PrintWriter 等,它们都是抽象装饰类。
2.4 装饰者优缺点
2.4.1 优点
- 装饰者模式提供了比继承更加符合开闭原则,具有更好的灵活性;因为继承是在静态期间确立关系,而装饰者是在运行时动态添加对象的功能;
- 解耦,通过增加子类方式来修改对象创建行为,这种通过增加而不是修改类的方式,是面向对象设计所推崇的。
2.4.2 缺点
- 会在系统中产生较多的类,造成系统臃肿;
- 使用难度较高,对设计人员抽象能力要求较高。
2.5 装饰者模式的应用场景
- 当需要给一个现有类添加附加职责,而又不能采用生成子类的方法进行扩充时。例如,该类被隐藏或者该类是终极类或者采用继承方式会产生大量的子类。
- 当需要通过对现有的一组基本功能进行排列组合而产生非常多的功能时,采用继承关系很难实现,而采用装饰器模式却很好实现。
- 当对象的功能要求可以动态地添加,也可以再动态地撤销时。