1、产生原因
此设计模式的产生是为了解决一个问题:扩展类的行为和方法的问题。我们知道,如果想扩展一个类或者对象的行为,可以采用继承的方式来实现,但是这个方式有局限性,继承是静态的,在编译的时候子类的行为已经是确定的了,不便于控制增加行为的方式和时机。
修饰者模式的产生就是为了解决上述问题,此模式可以将一个对象嵌入到另一个对象中,由另一个对象来决定是否引用该对象来扩展自己的行为。这是一种动态的方式,我们可以在应用程序中动态的控制。
2、定义
动态地给一个对象增加一些额外的职责,是一种灵活拓展功能的方案。
3、模式结构
装饰者模式中的角色:
Component(抽象构件)
它是具体构件和抽象装饰类的共同父类,声明了在具体构件中实现的业务方法,它的引入可以使客户端以一致的方式处理未被装饰的对象以及装饰之后的对象,实现客户端的透明操作。
ConcreteComponent(被装饰者,具体构件)
它是抽象构件类的子类,用于定义具体的构件对象,实现了在抽象构件中声明的方法,装饰器可以给它增加额外的职责(方法)。
Decorator(抽象装饰类)
它也是抽象构件类的子类,用于给具体构件增加职责,但是具体职责在其子类中实现。它维护一个指向抽象构件对象的引用,通过该引用可以调用装饰之前构件对象的方法,并通过其子类扩展该方法,以达到装饰的目的。
ConcreteDecorator(具体装饰类)
它是抽象装饰类的子类,负责向构件添加新的职责。每一个具体装饰类都定义了一些新的行为,它可以调用在抽象装饰类中定义的方法,并可以增加新的方法用以扩充对象的行为。
由于具体构件类和装饰类都实现了相同的抽象构件接口,因此装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任,换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不需要创造更多子类的情况下,将对象的功能加以扩展。
4、案例分析
案例来自于《Head First设计模式》:
在星巴克里面购买咖啡时,可以要求在其中加入各种调料,星巴兹会根据所加入的调料收取不同的费用,也就是说不同的咖啡与调料之间有多种组合方式,每种咖啡和调料都有不同的收费,设计一种方案,当用户选择了咖啡和调料后,能够计算出总费用,同时满足咖啡和调料的灵活组合性和易拓展性。
(1)抽象构件类Component,此处定义为Beverage.java
public abstract class Beverage {
protected String description = "Unknown Beverage";
public String getDescription(){
return description;
}
public abstract double cost();
}
(2)具体构件类ConcreteComponent,此处表示被修饰的类,被增加功能和方法的类,此处定义一个DarkRoast,可以定义多个,一个类代表一种可以被修饰的类。
public class DarkRoast extends Beverage {
public DarkRoast(){
description = "DarkRoast";
}
@Override
public double cost(){
return 1.05;
}
}
(3)抽象修饰类Decorator,此处定义为Decorator ,注意抽象修饰类和具体构件类都要继承抽象构件类,此处都要继承Beverage。
public abstract class Decorator extends Beverage {
public abstract String getDescription();
}
(4)具体的修饰者,此处定义两个,分别是Mocha 和Soy ,两个具体修饰者都继承了抽象修饰者,而且都包含了抽象构件类型的属性,这样的话就可以在其方法中,调用实现了抽象构件类型的类或者对象的方法,再结合本身的方法,最终达到多个类中方法的组合,实现了扩展功能的目的。
public class Mocha extends Decorator {
Beverage beverage;
public Mocha(Beverage beverage){
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + " , Mocha";
}
@Override
public double cost() {
return beverage.cost() + 0.20;
}
}
public class Soy extends Decorator{
Beverage beverage;
public Soy(Beverage beverage){
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + " , Soy";
}
@Override
public double cost() {
return beverage.cost() + 0.10;
}
}
(5)主类
public class MainTest {
public static void main(String[] args) {
//被修饰类,一个具体的Component对象上
Beverage beverage2 = new DarkRoast();
System.out.println(beverage2.getDescription() + " $" + beverage2.cost());
//加了一次Mocha配料,在Mocha对象中,调用了beverage2的cost方法,实现了对beverage2拓展
beverage2 = new Mocha(beverage2);
System.out.println(beverage2.getDescription() + " $" + beverage2.cost());
//又加了一次Mocha配料,在Mocha对象中,调用了beverage2的cost方法,实现了对beverage2拓展
beverage2 = new Mocha(beverage2);
System.out.println(beverage2.getDescription() + " $" + beverage2.cost());
//加了一次Soy配料,在Soy对象中,调用了beverage2的cost方法,实现了对beverage2拓展
beverage2 = new Soy(beverage2);
System.out.println(beverage2.getDescription() + " $" + beverage2.cost()+"修饰333333");
}
}
5、总结
(1)抽象构件、具体构件(被修饰类)、抽象修饰类和具体修饰类四个角色要全,其中具体构件和抽象修饰类都要继承抽象构件类
(2)具体修饰类中要包含一个抽象构件类类型的属性,抽象类变量可以引用实现了抽象类的类的变量,可以容易拓展,不易出错。