定义
装饰者模式和继承的作用类似,但优于继承。继承往往需要修改基类,装饰者模式支持在不修改任何底层代码的情况下,为对象赋予新的职责。
设计原则:类应该对扩展开放,对修改关闭。
简单的来说,就是在不修改现有代码的基础上,进行功能扩展。
目标:允许类容易扩展,在不修改现有代码的情况下,就可以搭配新的行为。
装饰的思想
以做一杯咖啡为例,比如现在我们要做一杯摩卡和奶泡的深度烘焙咖啡,步骤如下:
- 拿一个深度烘焙咖啡的对象
- 用摩卡对象装饰它
- 用奶泡对象装饰它
- 调用cost()方法,并依赖委托,将调料的加上去
就是要以饮料为主体,在运行时用调料来对饮料进行装饰,以生成所需的咖啡。主要流程如图。
UML如图。图中的线,均表示继承关系。
装饰者模式:动态的将责任附加到对象上,提供了比继承更有弹性的替代方案。
代码示例
总基类 —— Beverage(抽象类)
public abstract class Beverage {
String description = "Unknown Beverage";
public String getDescription() {
return description;
}
public abstract double cost();
}
装饰者基类 —— CondimentDecorator(抽象类)
public abstract class CondimentDecorator extends Beverage {
Beverage beverage;
// 装饰者必须全部重新实现 getDescription()
public abstract String getDescription();
}
咖啡底料举例(深度烘焙咖啡) —— DarkRoast
public class DarkRoast extends Beverage {
public DarkRoast() {
description = "Dark Roast Coffee";
}
public double cost() {
return .99;
}
}
调料举例(牛奶) —— Milk
public class Milk extends CondimentDecorator {
// 咖啡底以构造器入参形式注入
public Milk(Beverage beverage) {
this.beverage = beverage;
}
public String getDescription() {
return beverage.getDescription() + ", Milk";
}
public double cost() {
return .10 + beverage.cost();
}
}
下订单
public static void main(String args[]) {
Beverage beverage = new Espresso();
System.out.println(beverage.getDescription() + " $" + beverage.cost());
Beverage beverage2 = new DarkRoast();
beverage2 = new Mocha(beverage2);
beverage2 = new Mocha(beverage2);
beverage2 = new Whip(beverage2);
System.out.println(beverage2.getDescription() + " $" + beverage2.cost());
Beverage beverage3 = new HouseBlend();
beverage3 = new Soy(beverage3);
beverage3 = new Mocha(beverage3);
beverage3 = new Whip(beverage3);
System.out.println(beverage3.getDescription() + " $" + beverage3.cost());
}
现有装饰者举例
Java的I/O,如下图。与我们的咖啡例子非常相似。
延伸
抽象类
抽象类和接口都不能直接实例化,抽象类要被子类继承,接口要被类实现。抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。
特点:
- 抽象类无法被实例化
- 子类继承抽象类然后实例化子类
- 子类继承了抽象类剋必须实现抽象类的中的抽象方法
作用:
- 作为模板而存在,定义一组子类共有的属性和方法
- 强制子类必须重写抽象方法
代码执行顺序
- 父类静态对象,父类静态代码块
- 子类静态对象,子类静态代码块
- 父类非静态对象,父类非静态代码块
- 父类构造函数
- 子类非静态对象,子类非静态代码块
- 子类构造函数