设计模式之装饰者模式
装饰者模式
引入一个例子,比如去咖啡店喝咖啡,有些人喜欢不加糖不加牛奶的咖啡,有些人喜欢和加糖但是不加牛奶,还有些人喜欢和加牛奶但不加糖,亦有人喜欢两者都加。
先用 Java 的继承方式看一看能不能很好的解决这个问题,首先定义一个咖啡类
public class Coffe {
// 咖啡是否加配料描述
protected String description;
// 咖啡的价钱
protected Double cost;
// 默认咖啡什么都不加
// 价格 10 块
public Coffe(){
this.description = "咖啡";
this.cost = 10.0;
}
// 返回咖啡加配料的描述
public String getDescription(){
return description;
}
// 返回价格
public double getCost(){
return cost;
}
}
然后定义加糖、加牛奶、加牛奶和糖的子类
加糖的子类:
public class CoffeWithSugar extends Coffe {
public CoffeWithSugar(){
super.description = "加糖的咖啡";
}
@Override
public double getCost() {
return super.getCost() + 2;
}
}加牛奶的子类:
public CoffeWithMilk(){
super.description = "加牛奶的咖啡";
}
@Override
public double getCost() {
return super.getCost() + 2;
}
}
既加糖又加牛奶的子类
public class CoffeWithSugarMilk extends Coffe{
public CoffeWithSugarMilk(){
super.description = "加糖加牛奶的咖啡";
}
@Override
public double getCost() {
return super.getCost() + 6;
}
} 现在需要加什么的咖啡就只需要直接 new 相关的对象就可以了。感觉好像 Java 的继承也可以很好的解决这个问题,那要什么设计模式,好好写代码不香吗?非要整这么多花里胡哨的设计模式?难道设计模式是用来装X的?
非也非也,如果牛奶涨价了,是不是需要重新修改 CoffeWithMilk 和 CoffeWithSugarMilk 这两个类,违反了开闭原则(开闭原则:软件实体(类、模块、方法)应该对扩展开发,对修改关闭),每次一有价格的波动就需要两个及以上的类。如果以后添加其他配料的话还要添加更多的子类,如果有变动那么就要修改更多的类,这对后期维护造成很大的麻烦。
下面主角登场——装饰者模式,首先,装饰者模式有一些关键点,根据《Head First Design Pattern》中的描述:
- 装饰者和被装饰者必须有相同的父类。
- 可以用一个或多个装饰者装饰一个对象。
- 既然装饰者和被装饰者有相同的父类,那么任何出现原始对象(被装饰者)的地方,都可以使用装饰过得对象代替。
- 装饰者可以在所委托被装饰者的行为之前或之后,加上自己的行为,以达到特定的目的。
- 对象可以在任何时候被装饰,所以可以在运行时动态地、不限量地用你喜欢的装饰者来修饰对象。
以上就是使用装饰者模式需要注意的点,但这么看有点抽象,不太好理解。先上例子:
首先定义一个咖啡的抽象类,因为第一点:装饰者和被装饰者必须有相同的父类
public abstract class ACoffe {
private String description;
private Double cost;
protected abstract String getDescription();
protected abstract double getCost();
}咖啡的实现类
public class Coffe extends ACoffe{
@Override
protected String getDescription() {
return "咖啡";
}
@Override
protected double getCost() {
return 10;
}
}装饰父类,这个装饰父类也可以定义为实现类,但是为了更好(好像并没有)的演示装饰者模式,在这里就定义为抽象类了。
public abstract class AbstractDecorator extends ACoffe{
protected ACoffe aCoffe;
public AbstractDecorator(ACoffe aCoffe){
this.aCoffe = aCoffe;
}
@Override
protected abstract String getDescription();
@Override
protected abstract double getCost();
}糖的装饰类
public class SugarDecorator extends AbstractDecorator {
public SugarDecorator(ACoffe aCoffe) {
super(aCoffe);
}
@Override
protected String getDescription() {
return super.aCoffe.getDescription() + " 加糖";
}
@Override
protected double getCost() {
return super.aCoffe.getCost() + 2;
}
}牛奶的装饰类
public class MilkDecorator extends AbstractDecorator {
public MilkDecorator(ACoffe aCoffe) {
super(aCoffe);
}
@Override
protected String getDescription() {
return super.aCoffe.getDescription() + " 加牛奶";
}
@Override
protected double getCost() {
return super.aCoffe.getCost() + 4;
}
}测试,这里可以加任意多次糖和牛奶,也可以什么都不加
public static void main(String[] args) {
ACoffe coffe = new Coffe();
coffe = new SugarDecorator(coffe);
coffe = new MilkDecorator(coffe);
System.out.println(coffe.getDescription() + " " + coffe.getCost());
}运行结果
咖啡 加糖 加牛奶 16.0
还有一种非常熟悉的写法
public static void main(String[] args) {
ACoffe coffe = new SugarDecorator(new MilkDecorator(new Coffe()));
System.out.println(coffe.getDescription() + " " + coffe.getCost());
}是不是有一种似曾相识的感觉,想一想,好好想一想在哪里见过。
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("test.txt"));是不是这个,其实 Java I/O 流中的包装流就是用的装饰者模式。
总结:
装饰者模式与类继承的比较
装饰者模式:
- 用于扩展 对象 的功能
- 不需要子类
- 可以动态扩展
- 每个装饰类有特定的职责
- 有效防止子类过多而导致混乱
- 灵活
类继承
- 用于扩展 类 的功能
- 需要子类
- 编译时分派职责
- 会有很多子类产生
- 不灵活
装饰者模式的定义与优缺点
装饰者模式在不改变原有类的基础上动态地将责任附加到对象上。提供了比继承更有弹性的替代方案。
优点:
灵活,不用修改原有类,可以选择不同的装饰类实现不同的效果,符合开闭原则
缺点
比继承复杂
这是第一次写博客,如果有写的不好的或者错误的地方,请各位大佬批评指正。
over! 睡觉

神州信息成长空间 29人发布