装饰者模式

装饰者模式动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

思维模式

首先需要明确一点:装饰者模式利用了“组合”的思想,避免了“继承”的滥用。
通过动态地组合对象,可以写新的代码添加新功能,而无需修改现有代码。

这就好比拼装一个机器人。
我们有各种各样的零部件:

  1. 躯干
  2. 类人的胳膊
  3. 带有钳子的胳膊
  4. 类人的腿
  5. 带轮子的腿
  6. 带履带的腿
  7. ……

简单粗暴的实现

想要设计各式各样的机器人,当然我们可以选择,设计一个Robot超类,然后让子类继承这个超类。
这样,我们可以得到:

  1. 躯干+人的胳膊+履带腿
  2. 躯干+钳子胳膊+轮子腿
  3. 躯干+4个人的胳膊+履带腿
  4. ……

根据排列组合的原理,如果我们需要,Robot的子类可以有无限多个。
假如我们从Robot父类处继承了一个cost()方法,这个方法根据该子类的component(组件)个数和数量来计算成本(cost),那么我们的cost()方法可以说要重写无数次——因为每一个子类的情况都是不一样的。

“装饰者模式”的实现

我们可以先为每一个零部件(component)确定成本,然后根据需要,动态地组装(组合)一个机器人。然后将所有的成本加起来。如此一来,不仅可以得到一个符合我们需求的机器人对象,更能很方便地计算cost。

  1. 躯干 $100
  2. 类人的胳膊 $40
  3. 带有钳子的胳膊 $50
  4. 类人的腿 $70
  5. 带轮子的腿 $70
  6. 带履带的腿 $50
  7. ……

组合一个有躯干+类人的胳膊+带履带的腿的机器人的cost也就是:
100 + 40 + 50 = $190;

实现组合

所谓组合,也就是我中有你

如图所示:


通过保有各个对象的引用,即可实现“组合”

对于这样一个组合来的Robot对象,我们不妨称其为:“robotTom”。
想要求得总成本,可以直接调用robotTom.cost();

这是怎样实现的呢?
如图所示:

用公式来表示就是:

[robotTom].cost() = [arm + body].cost() + leg.cost() = [body].cost() + arm.cost() + leg.cost() =  body.cost() + arm.cost() + leg.cost();

这里用“leg对象包住arm对象”来形容leg对象中有arm对象的引用,不过对于一个机器人而言,“leg能包住arm”好像有点魔幻现实的味道。

再举一例

我们不妨再举一个更容易理解的例子:

我们知道,各类绘图软件中都有图层(layer)的概念。
每一个图层都相当于一层透明纸,我们可以在上面任意地画东西,而不会影响其他图层。
把画着东西的图层一层一层地叠放(相当于“包装”)起来,我们就可以得到各式各样的画作。
此时,位于上一层的图层就相当于装饰者,而所谓的背景图层就相当于被装饰者

类图


其中,

  1. LayerLayerDecorator为抽象类
  2. LayerDecorator本质上也是Layer
  3. description是各个layer对象的自我描述
  4. position()表示layer对象在某个地方画一个图形
  5. RedBackgroundLayerBlueBackgroundLayer相当于被装饰者

对应的代码:
Layer.java:

public abstract class Layer {    String description = "我是抽象layer父类";    public String getDescription() {        return description;    }    public abstract String position();}

RedBackgroundLayer.java

public class RedBackgroundLayer extends Layer {    public RedBackgroundLayer (){        //从抽象父类layer继承来的description        description = "我是RedBackgroundLayer->" ;    }    @Override    public String position() {        return "我在底层画一个红色的layer->";    }}

BlueBackgroundLayer.java

public class BlueBackgroundLayer extends Layer {    public BlueBackgroundLayer() {        description = "我是一个BlueBackgroundLayer";    }    @Override    public String position() {        return null;    }}

各个装饰者:
RectangleLayerDecorator.java

public class RectangleLayerDecorator extends LayerDecorator {    Layer layer;     //constructor    public RectangleLayerDecorator(Layer layer) {//这一步很重要        this.layer = layer;    }    @Override    public String getDescription() {        return layer.getDescription() + "RectangleLayerDecorator->";    }    @Override    public String position() {        return layer.position() + "左上角画□->";    }}

TriangleLayerDecorator.java

public class TriangleLayerDecorator extends LayerDecorator{    Layer layer;    //constructor    public TriangleLayerDecorator(Layer layer) {        this.layer = layer;    }    @Override    public String getDescription() {        return layer.getDescription() + "TriangleLayerDecorator->";    }    @Override    public String position() {        return layer.position() + "右上角画△->";    }}

RoundLayerDecorator.java

public class RoundLayerDecorator extends LayerDecorator {    Layer layer;    //constructor    public RoundLayerDecorator(Layer layer) {        this.layer = layer;    }    @Override    public String getDescription() {        return layer.getDescription() + "RoundLayerDecorator->";    }    @Override    public String position() {        return layer.position() + "右下角画○->";    }}

运行Demo

RunDemoTest.java

public class RunDemoTest {    public static void main (String[] args ){//Step 1        Layer backgroundLayer = new RedBackgroundLayer();        System.out.println(backgroundLayer.getDescription() + backgroundLayer.position());//Step 2        //为RedBackgroundLayer装饰一个TriangleLayerDecorator        Layer multipleLayer = new TriangleLayerDecorator(backgroundLayer);//Step 3        //再装饰一个RectangleLayerDecorator        multipleLayer = new RectangleLayerDecorator(multipleLayer);//Step 4        //再装饰一个RoundLayerDecorator        multipleLayer = new RoundLayerDecorator(multipleLayer);        System.out.println(multipleLayer.getDescription() + multipleLayer.position());    }}

运行结果:

我是RedBackgroundLayer->我在底层画一个红色的layer->我是RedBackgroundLayer->TriangleLayerDecorator->RectangleLayerDecorator->RoundLayerDecorator->我在底层画一个红色的layer->右上角画△->左上角画□->右下角画○->

运行过程示意图:


最终得到: