继承是拓展类功能最常见的手段,但是其缺点也很明显,其耦合程度较高。这里我们介绍一种新的方法来拓展类的功能——Decorator Pattern装饰器模式,其是结构性模式的一种,通过包装的方式实现动态增强、拓展已有对象的功能
设计模式 |
---|
![]() |
简介
在 Decorator Pattern 装饰器模式下,如果我们期望拓展已有对象的功能,那么就可以通过装饰者对象来包装该这个已有对象来实现。显然这种动态的方式比继承会更加灵活。具体地,装饰者对象内部会持有被装饰对象的引用,这样装饰者对象一方面用于实现拓展的功能,另一方面其会再将请求转发给被装饰的对象。在Java的IO流中就大量使用了该模式
这里我们以奔驰车Car为例来进行引入,对于一个BenzCar对象来说,其具备的基本功能就是乞丐版的Car。但实际上不同喜好的客户可能需要添加不同的配置。比如有人要加配座椅,有人要加配影音,还有人要加运动套件。试想一下,如果用继承的方式来实现不同配料组合下的各种Car。可以想见,整个子类将会非常庞大臃肿。那么这个时候,我们就可以通过装饰器模式来优雅的解决这个问题。在装饰器模式下,其通常有以下几个角色
- 「抽象组件接口」 :被装饰对象 与 装饰者对象 需要实现同一个接口,以便装饰者对象拓展、增强被装饰对象的功能、行为。本文的例子下,就是一个Car接口,其定义了生成create()的方法,用于烹饪出一个Car
- 「被装饰的具体对象」:一个实现上述接口的具体类,其通常是按基础、通用的原则来实现接口中定义的方法。在这里即为BenzCar类,其create只制造了一个乞丐版的Car
- 「抽象装饰者」:在抽象装饰者中,其内部会持有被装饰对象引用来转发请求,在Java中一般通过抽象类来定义该角色。在本文实例中即为CarDecorator类
- 「具体装饰者」:其是抽象装饰者的实现类,用于实现拓展的功能。在本文例子中,即为LeatherChairCarDecorator、VideoCarDecorator、SportCarDecorator类。用于给乞丐版的Car添加各种配置
实现
现在我们利用Java来实现本文所说的例子,首先我们定义一个被装饰者、装饰者共有的接口
/**
* 抽象组件接口:Car
*/
public interface Car {
/**
* 创建Car
*/
void create();
}
然后来实现一个被装饰的具体对象BenzCar类,可以看到其只是创建一个啥配置都没有的BenzCar
/**
* 具体组件, 被装饰对象: BenzCar
*/
public class BenzCar implements Car {
/**
* 创建Car
*/
@Override
public void create() {
System.out.println("奔驰车!");
}
}
现在我们需要来给Car加各种配置了,首先定义一个抽象装饰者。可以看到其有一个car字段用于持有被装饰包装的对象
/**
* 抽象装饰者
*/
public abstract class CarDecorator implements Car {
protected Car car;
public CarDecorator(Car car) {
this.car = car;
}
@Override
public void create() {
if (car != null) {
car.create();
}
}
}
然后通过具体的装饰者来实现给Car添加不同的配置,这里我们提供了三个装饰者,分别用于给Car添加真皮座椅,影音系统、运动套件
/**
* 具体装饰者:给car加真皮座椅
*/
public class LeatherChairCarDecorator extends CarDecorator {
public LeatherChairCarDecorator(Car car) {
super(car);
}
@Override
public void create() {
System.out.print("加真皮座椅,");
super.create();
}
}
/**
* 具体装饰者:给car加运动套件
*/
public class SportCarDecorator extends CarDecorator {
public SportCarDecorator(Car car) {
super(car);
}
@Override
public void create() {
System.out.print("加运动套件,");
super.create();
}
}
/**
* 具体装饰者:给car加影音系统
*/
public class VideoCarDecorator extends CarDecorator {
public VideoCarDecorator(Car car) {
super(car);
}
@Override
public void create() {
System.out.print("加影音系统,");
super.create();
}
}
现在,我们来实际测试下,看看通过装饰器模式能不能拿到顶配的奔驰车。可以看到由于被装饰对象、装饰者都实现了同一个接口,所以可以包装多层整一个至尊版车
/**
* 测试用例
*/
public class DecoratorPatternDemo {
public static void main(String[] args) {
Car car = new BenzCar();
car.create();
// 给乞丐版Car加个真皮座椅
Car leatherChairCa = new LeatherChairCarDecorator(car);
leatherChairCa.create();
// 给真皮座椅Car再加个运动套件
Car sportPizza = new SportCarDecorator(leatherChairCa);
sportPizza.create();
// 给真皮座椅、运动Car再加个莹莹系统
Car superCar = new VideoCarDecorator(sportPizza);
superCar.create();
}
}
测试结果 |
---|
![]() |
应用
装饰器模式在io中具有实际的应用,FilterInputStream实际上没有做任何事情,只是持有了InputStream对象,所以呢,比如我们常用的FileInputStream,它并没有缓冲功能,我们每次调用read,都会向操作系统发起调用索要数据。假如我们通过BufferedInputStream来装饰它,那么每次调用read,会预先向操作系统多拿一些数据,这样就不知不觉中提高了程序的性能。
输入流 |
---|
![]() |
输出流 |
---|
![]() |