装饰者模式主要用于动态的给一个对象添加一些额外的职责,就增加功能来说,装饰者模式比生成子类更为灵活。一般来说,继承可以来扩展类的功能,但与继承不同的是,通过装饰者模式,我们可以选择一个类的某个对象,对其进行修改,而不会影响这个类中其他的实例。继承会直接为类增加功能,而装饰者模式则会通过将对象与其他对象进行包装的方式将功能添加到类。
装饰者模式UML图:
在应用装饰者模式的时候,我们在对象周围添加装饰类以对对象进行功能扩展。装饰类能够在调用被装饰的实例之前或之后实现对其功能进行扩展。每次需要增加特定的功能时,被装饰对象都会通过其他对象实现功能扩展。
抽象组件(Component):给出一个抽象接口,用于能够动态添加功能的对象。
具体组件(ConcreteComponent):定义一个实现组件接口的对象。这是实际需要装饰的对象,但其对装饰的过程一无所知。
装饰器(Decorator):维护对组件对象的引用,并定义了与组件对象形式一致的接口。所以它包含对基础行为的引用,并实现同样的接口。因此,也可以作为抽象组件本身来对待。客户端代码与装饰器以及抽象组件之间的交互应当是一致的,也不会体现出任何的差别。
具体装饰器(ConcreteDecoratorA/B):实际上由其负责为抽象组件增加功能。它是从装饰器继承的类,并通过以新的公共方法的形式来增加一些额外的特定功能。
装饰者模式的创建步骤:
- 创建原始的抽象组件和装饰器之间的继承关系。
- 在装饰器类中,将抽象组件作为字段添加
- 将抽象组件传递到装饰器的构造函数中,并初始化抽象组件对象
- 在装饰器类中,重定向所有抽象组件的方法到抽象组件对象
- 在具体装饰器类中,重写所有抽象组件中需要扩展或修改的方法
装饰者模式的应用方法:
首先,定义一个接口。这个接口会为将要使用装饰器的类创建一个初步的框架。其次,实现该接口的基本功能。这样我们就有了一个接口和具体实现类。然后,创建一个包含(聚合关系)接口类型属性的抽象类。此类的构造函数将接口类型实例赋给该属性。此类是装饰器基类。那么我们就能够根据需要扩展这个类并创建想要的具体装饰器类。
具体装饰器类将会添加其自己的方法。这些方法的执行可以是在具体装饰器调用基类实例中的方法之前或者之后。这种装饰模式的关键是基于对象构造函数传递的参数,在运行时绑定方法和基类实例。它能对特定实例动态的实现功能定制。
代码实操:
一个房子的接口:
public interface House {
String makeHouse();
}
房子接口的具体实现:
public class SimpleHouse implements House {
@Override
public String makeHouse() {
return "Base house";
}
}
装饰者基类:装饰者模式的核心,包含一个接口House类型的属性。在使用其构造函数创建装饰器的过程中动态分配实例。一旦分配完就会调用实例方法
public abstract class HouseDecorator implements House {
protected House house;
public HouseDecorator(House house){
this.house = house;
}
public String makeHouse(){
return house.makeHouse();
}
}
下面两个类都是实现了抽象装饰器的具体装饰器类。当创建该装饰器时,基类实例通过构造函数传递参数并赋值给父类。在makeHouse方法中,我们调用基类方法以及自身的方法addColors()的组合,这个addColors()方法通过增加步骤实现扩展功能。
public class ColorDecorator extends HouseDecorator {
public ColorDecorator(House house) {
super(house);
}
private String addColors(){
return " +Colors";
}
@Override
public String makeHouse() {
return house.makeHouse()+addColors();
}
}
public class LightsDecorator extends HouseDecorator {
public LightsDecorator(House house) {
super(house);
}
private String addLights(){
return " +Lights";
}
@Override
public String makeHouse() {
return house.makeHouse()+addLights();
}
}
测试:
public class TestDecorator {
public static void main(String[] args) {
House house = new LightsDecorator(new ColorDecorator(new SimpleHouse()));
System.out.println(house.makeHouse());
}
}
装饰者模式的适用性:
- 需要扩展一个类的功能,或给类添加附加职责
- 需要动态的给一个对象添加功能
- 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变得不现实。
- 当不能采用生成子类的方法进行扩充时,一种情况是,可能有大量独立的扩展,为支持每一种组合而产生大量的子类,使得子类数目增长快速。另一种情况可能是引文版类定义被隐藏,或类定义不能用于生成子类。
优点:
- 装饰者模式与继承关系的目的都是要扩展对象的功能,但是装饰者模式可以提供比继承更多的灵活性
- 通过使用不同的具体装饰类以及这些装饰类的排列组合,我们可以创造出很多不同行为的组合。
缺点:
- 这种比继承更加灵活的特性,也同时意味着更加多的复杂性
- 装饰者模式会导致设计中出现很多小类,如果过度使用,会使程序变得很复杂
- 装饰者模式是针对抽象组件(Component)类型编程。但是,如果要针对具体组价编程时,就应该重新思考应用架构,以及装饰者是否合适。当然也可以改变Component接口,增加新的公开的行为,实现“半透明”的装饰者模式。