模式定义
给爱用继承的人一个全新的设计眼界——使用组合(has-A)达到复用效果。
装饰者模式(Decorator) 动态地将功能附加到对象上。若要扩展功能,装饰者提供了比继承更具弹性的方案。
- 装饰者和被装饰者具有相同的超类型(抽象类/接口)。
- 可以使用一个或多个装饰者包装一个对象。
- 由于装饰者和被装饰者具有相同的超类型,因此在任何需要原始对象的场合,都可以使用装饰者来代替它。
- 装饰者可以在被装饰者前、后加上自己的行为,或者直接覆盖。
- 装饰者对客户透明。
- 装饰者模式会导致设计中出现很多小对象。
设计原则
开闭原则。 类应该对扩展开放,对修改关闭。
问题场景
为咖啡店设计一个订单系统。咖啡店有基础的咖啡款式(
DarkRoast
、Espresso
等)和各种调料(milk
、soy
、whip
等)。当顾客下单时给出商品价格。
常规做法
咖啡里含有各个调料的引用,每个变量都有相应的hasXXX()
方法决定是否加上调料价格。
弊端为添加调料种类时需要加上新的方法并修改cost()
方法;有的饮料继承了不合适的方法,比如Tea不应该有hasWhip()
方法;顾客想要双份饮料时不好实现·······
装饰者模式做法
以饮料为主体,在运行时用调料“装饰”饮料。比如顾客想要摩卡奶泡深焙咖啡,那么要做的是:
- 拿一个深焙咖啡(DarkRoast)对象。
- 以摩卡(Mocha)对象装饰它。
- 以奶泡(Whip)对象装饰它。
- 调用
cost()
方法,逐层将调料价格加上去。
类图
代码
重点都在注释里
先来看看运行时的效果吧
public class DecoratorTest {
public static void main(String[] args) {
Component darkRoast = new DarkRoastCoffee();
darkRoast = new Mocha(darkRoast);
darkRoast = new Mocha(darkRoast);
darkRoast = new Whip(darkRoast);
System.out.println(darkRoast.getDes());
System.out.println(darkRoast.cost());
}
}
2.2
Whip, Mocha, Mocha, DarkRoastCoffee
//所有类都继承此类,不是为了继承行为,而是为了有正确的类型
public abstract class Component {
protected String des = "Component";
public String getDes() {
return des;
}
public abstract double cost();
}
//所有基础咖啡的父类
public class Coffee extends Component {
@Override
public double cost() {
return 0;
}
@Override
public String getDes() {
return super.getDes();
}
public Coffee() {
this.des = "coffee";
}
}
//所有装饰者的父类
public abstract class Condi extends Component {
public abstract String getDes();
}
//深焙咖啡
public class DarkRoastCoffee extends Coffee {
public DarkRoastCoffee() {
this.des = "DarkRoastCoffee";
}
@Override
public double cost() {
return 1.3;
}
}
//摩卡调料
//具体装饰者
public class Mocha extends Condi {
private Component component;
//创建实例时注册被装饰者
//参数类型为Component,而不是coffee或者condi,因为要对两者都进行装饰
public Mocha(Component component) {
this.component = component;
this.des = "Mocha";
}
@Override
public String getDes() {
return this.des + ", " + component.getDes();
}
//委托调用计算价格
@Override
public double cost() {
return component.cost() + 0.2;
}
}
真实世界的装饰者:Java I/O
FilterInputStream是所有InputStream的抽象装饰者
- DataInputStream
- BufferedInputStream
将低层流添加到缓冲区。