23种设计模式之装饰模式
参考资料
- Java设计模式:23种设计模式全面解析(超级详细)
- 韩顺平老师的Java设计模式(图解+框架源码剖析)
- 秦小波老师的《设计模式之禅》
下文如有错漏之处,敬请指正
一、简介
定义
动态的给一个对象增加额外的功能。
特点
- 装饰者模式是一种结构型模式
- 在对象扩展方面比继承更有弹性
- 装饰者模式是从最里层(被装饰者)开始向外(装饰者)一一执行
通用类图
主要角色:
-
Component
抽象主体角色(如咖啡)
-
ConcreteComponent
具体主体角,被装饰者(如美式、摩卡、卡布奇诺)
继承主体
-
Decorator
抽象装饰者角色
继承抽象主体并聚合抽象主体
-
ConcreteDecorator
具体装饰者角色(巧克力、牛奶、芝士)
继承抽象装饰者
优点
-
装饰类和被装饰类都可以独立发展,不会相互耦合。
-
装饰模式是继承关系的一个替代方案。装饰类Decorator,不管装饰多少层,返回的对象还是Component,实现的还是is-a的关系。
缺点
- 多层装饰比较复杂。
应用场景
- JDK中的 IO
- 对象与对象之间发生逻辑上的组合关系(给不同权限用户分配不同功能,用户是被装饰者,功能是装饰者)
- 动态增加功能,动态撤销。
二、装饰者模式
需求:
现在有一个咖啡馆要做一个订单系统,计算顾客所点的咖啡和调料的价格。咖啡有:摩卡、卡布奇诺、拿铁。调味品有:巧克力、牛奶、芝士。
抽象主体:
package decorator;
public abstract class Coffee {
// 定义咖啡的描述信息
private String desc;
// 定义咖啡的价格
private float cost;
public String getDesc() {
return this.desc + " " + this.cost;
}
public void setDesc(String desc) {
this.desc = desc;
}
public void setCost(float cost) {
this.cost = cost;
}
//计算费用的抽象方法
public float cost() {
return this.cost;
}
}
具体主体:
package decorator;
public class Cappuccino extends Coffee {
public Cappuccino() {
this.setDesc("卡布奇诺");
this.setCost(21.00f);
}
}
package decorator;
public class Latte extends Coffee {
public Latte() {
this.setDesc("拿铁");
this.setCost(23.00f);
}
}
package decorator;
public class Mocha extends Coffee {
public Mocha() {
this.setDesc("摩卡");
this.setCost(22.00f);
}
}
抽象装饰者
package decorator;
public abstract class Decorator extends Coffee {
private Coffee coffee;
public Decorator(Coffee coffee) {
this.coffee = coffee;
}
@Override
public String getDesc() {
return coffee.getDesc() + " + " + super.getDesc();
}
@Override
public float cost() {
return coffee.cost() + super.cost();
}
}
具体装饰者:
package decorator;
public class Chess extends Decorator {
public Chess(Coffee coffee) {
super(coffee);
this.setDesc("芝士");
this.setCost(10.00f);
}
}
package decorator;
public class Chocolate extends Decorator {
public Chocolate(Coffee coffee) {
super(coffee);
this.setDesc("巧克力");
this.setCost(8.00f);
}
}
package decorator;
public class Milk extends Decorator {
public Milk(Coffee coffee) {
super(coffee);
this.setDesc("牛奶");
this.setCost(6.00f);
}
}
Client:
package decorator;
public class Client {
public static void main(String[] args) {
Coffee coffee = new Cappuccino();
System.out.println("Desc: " + coffee.getDesc());
System.out.println("Cost: " + coffee.cost() + "¥");
coffee = new Milk(coffee);
System.out.println("Desc: " + coffee.getDesc());
System.out.println("Cost: " + coffee.cost() + "¥");
coffee = new Chocolate(coffee);
System.out.println("Desc: " + coffee.getDesc());
System.out.println("Cost: " + coffee.cost() + "¥");
coffee = new Chess(coffee);
System.out.println("Desc: " + coffee.getDesc());
System.out.println("Cost: " + coffee.cost() + "¥");
/**
* 输出结果:
* Desc: 卡布奇诺 21.0
* Cost: 21.0¥
* Desc: 卡布奇诺 21.0 + 牛奶 6.0
* Cost: 27.0¥
* Desc: 卡布奇诺 21.0 + 牛奶 6.0 + 巧克力 8.0
* Cost: 35.0¥
* Desc: 卡布奇诺 21.0 + 牛奶 6.0 + 巧克力 8.0 + 芝士 10.0
* Cost: 45.0¥
*/
}
}
当要增加一种新咖啡品种时,只需要继承抽象主体,当增加一种调味品时,只需要继承抽象装饰者,不改变原有结构。
三、总结
- 装饰模式是对继承的有力补充。继承是静态地给类增加功能,而装饰模式则是动态地增加功能。
- 装饰模式还有一个非常好的优点——扩展性非常好。