设计模式之装饰者模式
装饰者模式
引入一个例子,比如去咖啡店喝咖啡,有些人喜欢不加糖不加牛奶的咖啡,有些人喜欢和加糖但是不加牛奶,还有些人喜欢和加牛奶但不加糖,亦有人喜欢两者都加。
先用 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! 睡觉