继承是拓展类功能最常见的手段,但是其缺点也很明显,其耦合程度较高。这里我们介绍一种新的方法来拓展类的功能——Decorator Pattern装饰器模式,其是结构性模式的一种,通过包装的方式实现动态增强、拓展已有对象的功能
简介
在 Decorator Pattern 装饰器模式下,如果我们期望拓展已有对象的功能,那么就可以通过装饰者对象来包装该这个已有对象来实现。显然这种动态的方式比继承会更加灵活。具体地,装饰者对象内部会持有被装饰对象的引用,这样装饰者对象一方面用于实现拓展的功能,另一方面其会再将请求转发给被装饰的对象。在Java的IO流中就大量使用了该模式
这里我们以肯德基的Pizza为例来进行引入,对于一个KFCPizza对象来说,其具备的基本功能就是烹饪一个乞丐版的Pizza。但实际上不同口味的客户可能需要添加不同的配料。比如有人要加牛肉,有人要加榴莲,还有人要加水果。试想一下,如果用继承的方式来实现不同配料组合下的各种Pizza。可以想见,整个子类将会非常庞大臃肿。那么这个时候,我们就可以通过装饰器模式来优雅的解决这个问题。在装饰器模式下,其通常有以下几个角色
- 抽象组件接口 :被装饰对象 与 装饰者对象 需要实现同一个接口,以便装饰者对象拓展、增强被装饰对象的功能、行为。本文的例子下,就是一个Pizza接口,其定义了cook方法,用于烹饪出一个Pizza
- 被装饰的具体对象:一个实现上述接口的具体类,其通常是按基础、通用的原则来实现接口中定义的方法。在这里即为KFCPizza类,其cook方法只烹饪了一个乞丐版的Pizza
- 抽象装饰者:在抽象装饰者中,其内部会持有被装饰对象引用来转发请求,在Java中一般通过抽象类来定义该角色。在本文实例中即为PizzaDecorator类
- 具体装饰者:其是抽象装饰者的实现类,用于实现拓展的功能。在本文例子中,即为BeefPizzaDecorator、DurianPizzaDecorator类。用于给乞丐版的Pizza添加各种配料
实现
现在我们利用Java来实现本文所说的例子,首先我们定义一个被装饰者、装饰者共有的接口
1 | /** |
然后来实现一个被装饰的具体对象KFCPizza类,可以看到其只是烹饪一个啥配料都不加的Pizza
1 | /** |
现在我们需要来给Pizza加各种配料了,首先定义一个抽象装饰者。可以看到其有一个pizza字段用于持有被装饰包装的对象
1 | /** |
然后通过具体的装饰者来实现给Pizza添加不同的配料,这里我们提供了两个装饰者,分别用于给Pizza添加牛肉、榴莲
1 | /** |
现在,我们来实际测试下,看看通过装饰器模式能不能吃到牛肉披萨。可以看到由于被装饰对象、装饰者都实现了同一个接口,所以可以包装多层整一个至尊披萨superPizza
1 | /** |
测试结果符合预期
参考文献
- Head First 设计模式 弗里曼著