本文讲下什么是装饰模式。装饰模式类图如下:
这里有父组件Component是一个抽象类或者一个接口,这个看具体业务的需求,ConcreteComponent是一个继承了父类(接口)的具体实现类,也就是被装饰的类。Decorator是装饰器,其实也是可有可无的,看具体业务,如果装饰器是一个抽象类,辣么它的存在就表示子类必须实现装饰器的某些方法,如果装饰器的某些方法已经自己实现了,说明子类可以实现也可以不实现其方法。装饰器下面的就是实际的用来装饰ConcreteComponent的装饰材料。
看一个具体例子
代码类之间的关系如下:
解释:第一部分,是父类,之所以要这个父类是因为Mocha或者soy内部持有一个父类的引用,只有持有这个引用,才可以既覆盖父类(通过继承)的方法又装饰父类(通过调用父类的引用的相同的方法(不同的方法也可以调))的方法。第二部分,就是已经实现了父类大部分方法的子类,也就是被装饰的类(通过Mocha和Soy来增强)。第三部分,一个装饰器,用来表述哪些方法需要子类去实现(抽象类)。第四部分,装饰第二部分的类,可以在第二部分类的基础上,进行二次开发。
代码如下:
public abstract class Beverage {
protected String description = "Unknown Beverage";
public String getDescription() {
return description;
}
public abstract double cost();
}
public class DarkRoast extends Beverage {
public DarkRoast() {
description = "DarkRoast";
}
public double cost() {
return .99;
}
}
public abstract class CondimentDecorator extends Beverage {
public abstract String getDescription();
}
public class Mocha extends CondimentDecorator {
Beverage beverage;
public Mocha(Beverage beverage) {
this.beverage = beverage;
}
public String getDescription() {
return beverage.getDescription() + ", Mocha";
}
public double cost() {
return .20 + beverage.cost();
}
}
public class Whip extends CondimentDecorator {
Beverage beverage;
public Whip(Beverage beverage) {
this.beverage = beverage;
}
public String getDescription() {
return beverage.getDescription() + ", Whip";
}
public double cost() {
return .10 + beverage.cost();
}
}
Beverage beverage2 = new DarkRoast();
beverage2 = new Mocha(beverage2);
//这里传入的beverage2不是Beverage beverage2 = new DarkRoast();而是经过修饰后的beverage2 = new Mocha(beverage2);每次传入的都是经过修饰后的
beverage2 = new Mocha(beverage2);
beverage2 = new Whip(beverage2);
System.out
.println(beverage2.getDescription() + " $" + beverage2.cost());
运行结果:
DarkRoast, Mocha, Mocha, Whip $1.49
运行过程如下图所示:
下面解释下:
- 第一次new DarkRoast();的时候beverage2指向堆中的DarkRoast;
- 第二次beverage2 = new Mocha(beverage2)(第一个Mocha中有个成员变量Beverage beverage;),Mocha中的成员变量beverage指向了之前的beverage2,由于beverage2指向DarkRoast,也就是说Mocha中的成员变量beverage也指向DarkRoast。
- 再次,beverage2 = new Mocha(beverage2),第二个Mocha中的成员变量beverage也指向之前的beverage2 ,由于beverage2 还是指向第一个Mocha,所以第二个Mocha中的beverage也指向第一个Mocha,这里之所以都可以指向Mocha或者beverage2是因为他们发父类型是一样的。
- 最后,beverage2 = new Whip(beverage2);原理也是一样。其实,这里的beverage2只是用来接收不同的对象,保持一个引用。也可以每次都使用一个新的Beverage beverageNew来接收,这样的话,每次传入的就是这个新的beverageNew。使用每次new一个新对象的流程图如下:
打印结果也是一样。
当然,也可以这样写代码:
Beverage beverage2 = new DarkRoast();
beverage2 = new Whip(new Mocha(new Mocha(beverage2)));
System.out
.println(beverage2.getDescription() + " $" + beverage2.cost());
这样写的代码的流程图其实就是少了两条指向new出来的Mocha对象的虚线。