一、基本介绍
-
英文名:Decorator
-
概念:动态的将新功能附加到对象上。
-
模式类型:结构型
-
设计原则:开闭原则(OCP)
-
主要特点:在对象功能扩展方面,它比继承更有弹性
二、实现
需求: 下午茶:咖啡 + 调味剂,价格结算
- 咖啡种类多,调味剂种类也多
- 可单点咖啡,也可两者都点。且两者随意可组合(咖啡 + 多调味剂)
1、一般实现
方案一:该方案类会非常多,每增加一种咖啡或一种调味剂都需要创建很多类
- 抽象咖啡类,所有咖啡类都实现该抽象类
- 抽象调味剂,所有调味剂都实现该抽象类
- 给每一种咖啡和每一种调味剂的组合都创建一个类
方案二:该方案减少了类的创建,但是增加或删除一种调味剂都需要修改已有的所有咖啡类,不易扩展
- 抽象咖啡类,所有咖啡类都实现该抽象类
- 抽象调味剂,所有调味剂都实现该抽象类
- 给每一种咖啡都内置所有调味剂类
- 使用方法判断点的是哪种组合的咖啡
2、模式实现
-
抽象组件Component
-
具体实现组件ConcreateComponent继承Component
-
抽象装饰器Decorator继承Component,聚合Component(装饰器继承且聚合被装饰抽象类)
-
具体实现装饰器ConcreteDecorator继承Decorator
Ⅰ、代码
// 被装饰类 - 下午茶
public abstract class Drink {
private String name;
private BigDecimal price;
// 总结算费用
public abstract BigDecimal cost();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
}
// 被装饰的某一种类抽象
public abstract class Coffee extends Drink {
@Override
public BigDecimal cost() {
return super.getPrice();
}
}
// 被装饰的某一种类的具体实现
public class Latte extends Coffee {
// 可使用super,也可直接调用父类方法
public Latte() {
super.setName("拿铁");
setPrice(new BigDecimal("15.69"));
}
}
// 被装饰的某一种类的具体实现
public class Americano extends Coffee {
public Americano() {
setName("美式");
setPrice(new BigDecimal("23.99"));
}
}
// 装饰器类继承且聚合被装饰抽象类
public class Decorator extends Drink {
private Drink drink;
public Decorator(Drink drink) {
this.drink = drink;
}
/**
* @Description 递归计算费用(注入进的饮料价格 + 本身装饰器(调味品)的的价格)
* this.drink是构造器传入的对象
*/
@Override
public BigDecimal cost() {
return this.drink.cost().add(this.getPrice());
}
@Override
public String getName() {
return this.drink.getName() + " " + super.getName();
}
}
// 具体装饰类继承装饰器类
public class Milk extends Decorator {
public Milk(Drink drink) {
super(drink);
super.setName("牛奶");
setPrice(new BigDecimal("1.25"));
}
}
// 具体装饰类继承装饰器类
public class Sugar extends Decorator {
public Sugar(Drink drink) {
super(drink);
super.setName("糖");
setPrice(new BigDecimal("0.19"));
}
}
public class Test {
public static void main(String[] args) {
Drink drink = new Latte();
BigDecimal cost = drink.cost();
System.out.println(drink.getName() + ": " + cost + "元");
// 装饰者 装饰 被装饰抽象类的具体实现
drink = new Milk(drink);
cost = drink.cost();
System.out.println(drink.getName() + ": " + cost + "元");
drink = new Sugar(drink);
cost = drink.cost();
System.out.println(drink.getName() + ": " + cost + "元");
}
}
Ⅱ、优点
- 装饰类和被装饰类可以独立发展,而不会相互耦合
- 继承关系的一个替代方案,不管装饰多少层,返回的对象还是原来的对象
- 可以动态地扩展一个实现类的功能
Ⅲ、缺点
- 多层的装饰是比较复杂的。就像剥洋葱一样,剥到了最后才发现是最里层的装饰出现了问题,想象一下工作量吧,因此,尽量减少装饰类的数量,以便降低系统的复杂度
三、使用场景
- 需要扩展一个类的功能,或给一个类增加附加功能
- 需要动态地给一个对象增加功能,这些功能可以再动态地撤销
- 需要为一批的兄弟类进行改装或加装功能,当然是首选装饰模式
1、常见应用场景
Ⅰ、IO流的FilterInputStream
- FilterInputStream继承InputStream抽象类,且聚合InputStream被装饰抽象类,即FilterInputStream充当装饰器