继续整理记录这段时间来的收获,详细代码可在我的Gitee仓库SpringBoot克隆下载学习使用!
5.4 装饰者模式
5.4.1 概述
在不改变现有对象结构的情况下,动态给对象增加某些职责或功能的模式
5.4.2 结构
- 抽象构件(Component):定义一个抽象接口以规范准备接受附加功能的对象
- 具体构件(Concrete Component):实现抽象构件,通过装饰角色为其添加功能后职责
- 抽象装饰(Decorator):继承或实现抽象构件,包含具体构件的实例,可通过其子类扩展具体构件中功能
- 具体装饰(Concrete Decorator):实现抽象装饰方法,给具体构件对象添加附加功能或职责
5.4.3 案例(快餐店)
- 快餐类–抽象构建类
public abstract class FastFood {
private float price;
private String description;
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public FastFood(float price, String description) {
this.price = price;
this.description = description;
}
public FastFood() {
}
public abstract float cost();
}
- 具体构建角色-炒饭
public class FiredRice extends FastFood{
public FiredRice() {
super(10,"炒饭");
}
@Override
public float cost() {
return getPrice();
}
}
- 具体构建角色-炒面
public class FiredNoodles extends FastFood{
public FiredNoodles()
{
super(12,"炒面");
}
@Override
public float cost() {
return getPrice();
}
}
- 鸡蛋类-具体装饰者
**/
public class Egg extends Garnish{
@Override
public float cost() {
// 计算价格 鸡蛋+快餐
return getPrice() + getFastFood().cost();
}
public Egg(FastFood fastFood) {
super(1, "鸡蛋", fastFood);
}
@Override
public String getDescription() {
return super.getDescription() + getFastFood().getDescription();
}
}
- 培根类-具体装饰者
public class Bacon extends Garnish{
@Override
public float cost() {
// 计算价格 培根+快餐
return getPrice() + getFastFood().cost();
}
public Bacon(FastFood fastFood) {
super(2, "培根", fastFood);
}
@Override
public String getDescription() {
return super.getDescription() + getFastFood().getDescription();
}
}
- 测试
public static void main(String[] args) {
// 点一份炒饭
FastFood firedRice = new FiredRice();
System.out.println(firedRice.getDescription() + firedRice.cost() + "元");
// 上面炒饭加蛋
firedRice = new Egg(firedRice);
System.out.println(firedRice.getDescription() + firedRice.cost() + "元");
// 上面炒饭加蛋
firedRice = new Egg(firedRice);
System.out.println(firedRice.getDescription() + firedRice.cost() + "元");
// 上面炒饭加培根
firedRice = new Bacon(firedRice);
System.out.println(firedRice.getDescription() + firedRice.cost() + "元");
}
- 结果
- 类图
5.4.4 优点
- 可以带来比继承更灵活的扩展功能,使用更加方便
- 可组合不同装饰着对象获取不同行为状态的多样化结果
- 装饰类和被装饰类可以独立发展,不会相互耦合
- 可以动态扩展一个实现类的功能
5.4.5 使用场景
- 当不能采用继承方法对系统进行扩充或采用继承不利于系统扩展和维护时:
- 不能采用继承情况主要有2类:
- 系统中存在大量按独立扩展,为支持每一种组合将产生大量子类,使得子类数目呈指数爆炸性增长
- 类定义不能被继承,如final类
- 在不影响其它对象情况下,以动态、透明方式给单个对象添加职责
- 当对象功能要求可以动态添加时或再动态撤销时
5.4.6 JDK源码
- IO流中的包装类使用了装饰者模式,BufferedInputStream,BufferedOutputStream,BufferedReader,BufferedWriter等类。
- 以BufferedWriter为例,说明简单使用BuffererWriter
public class Demo{
public static void main(String args[]) throw Exception{
//创建BufferedWriter对象
//创建FileWriter对象
FileWriter filWriter = new FileWriter("./a.txt");
BufferedWriter bufferedWriter = new BufferedWriter(filWriter);
//写数据
bufferedWriter.write("hello world");
bufferedWriter.close();
}
}
- 结构
BufferedWriter使用装饰者模式对Writer子类进行增强,添加缓冲区,提高数据读写效率。
5.4.7 静态代理与装饰区别
5.4.7.1 相同点
- 均要实现与目标类相同业务接口
- 在两个类中均要声明目标对象
- 均可以在不修改目标类前提下增加目标方法
5.4.7.2 不同点
- 目的不同
- 装饰者:增强目标对象
- 静态代理:保护和隐藏目标对象
- 获取目标对象构建地方不同
- 装饰者:由外界传来,可通过构造方法传递
- 静态代理:在代理类内部创建,以此来隐藏目标对象