当我们在设计一个一个类继承结构的时候,我们可能会存在以下的情况。首先有一种坦克的抽象类,那么这个坦克会有很多中类型,那么我们就派生这个坦克吧。比如说有德国虎式坦克,轻型坦克和工程坦克,那么现在又有三种功能要扩展到这三种坦克上,如两栖功能、夜视功能、雷达功能,可以把这三种功能设置为接口。按类继承的设计方案,如果需要这三种坦克每种坦克都有相应的功能坦克,我们需要继承Tank类,并实现相应功能的接口,那么我们的扩展类就有9种,这还不包括功能的组合,那么现在问题就出来了,这种类继承的模式会导致子类无限制的增加,这是我们无法忍受的。那么怎么解决这个问题呢?我们就需要Decorator模式来解决。
Decorator模式的动机:
上述描述的问题根源在于我们“过度地使用了继承来扩展对象的功能”,由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀(多继承)。如何使“对象功能的扩展”能够根据需要来动态地实现?同时避免“扩展功能的增多”带来的子类膨胀问题?从而使得任何“功能扩展变化”所导致的影响将为最低?
Decorator模式的意图:
动态地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类更为灵活。
如何用代码来实现Decorator模式:
首先我们有一个抽象基类Tank,里面有两个抽象方法Shot和Run
... {
public abstract void Shot();
public abstract void Run();
}
我们有三个派生类来表明三种坦克的类型,并实现了Shot和Run方法
... {
public override void Shot()
...{
}
public override void Run()
...{
}
}
public class LightTank : Tank
... {
public override void Shot()
...{
}
public override void Run()
...{
}
}
public class ConsturctTank : Tank
... {
public override void Shot()
...{
}
public override void Run()
...{
}
}
... {
private Tank _tank;//Has a对象组合
public Decorator(Tank tank)
...{
_tank = tank;
}
public override void Shot()
...{
_tank.Shot();
}
public override void Run()
...{
_tank.Run();
}
}
这是装饰接口的基类,他继承与Tank类,这里继承的Tank类不是一个is a的关系,他就像一个接口,规定了Decorator类要实现的方法。_tank作为一个Tank的实例,是一个Has a的对象组合关系。Decroator实现的Shot和Run方法是调用私有成员变量_tank的Shot和Run方法。
下面就是两栖功能的实现
... {
public Amphibian(Tank tank)
: base(tank)
...{
}
public override void Shot()
...{
//do some Externsion
base.Shot();
}
public override void Run()
...{
//do some extension
base.Run();
}
}
Amphibian类继承了Decorator类,在Amphibian类中的Shot和Run方法中,我们首先根据我们的功能,做一些shot和Run之前做一些相应的扩展,然后调用基类的Shot和Run方法,这样做我们即做了相应的扩展,又调用基本的Shot和Run方法。
我们还可以根据其他功能扩展做相关扩展的类,并继承Decorator类
如何来使用相关的功能呢?
... {
Tank tank = new TigerTank();
Amphibian am = new Amphibian(tank);
am.Shot();
}
在上面的代码中使用使用Amphibian来装饰tank,那么调用am.shot方法时,即扩展了两栖功能又使用了tank的原本的shot功能。如果现在还有一个红外功能要扩展,只需要实例化一个红外类,使用两栖类来作为参数,然后使用红外类的shot方法,那么这个shot即包含两栖功能又包含红外功能,并且还使用原来的shot功能。这里就是组合多种功能,也就是说使用多种功能来装饰tank。
Decorator模式的几个要点
- 通过采用组合、而非继承的手法, Decorator模式实现了在运行时动态地扩展对象功能的能力,而且可以根据需要扩展多个功能。避免了单独使用继承带来的“灵活性差”和“多子类衍生问题”。
- Component类在Decorator模式中充当抽象接口的角色,不应该去实现具体的行为。而且Decorator类对于Component类应该透明——换言之Component类无需知道Decorator类,Decorator类是从外部来扩展Component类的功能。
- Decorator类在接口上表现为is-a Component的继承关系,即Decorator类继承了Component类所具有的接口。但在实现上又表现为has-a Component的组合关系,即Decorator类又使用了另外一个Component类。我们可以使用一个或者多个Decorator对象来“装饰”一个Component对象,且装饰后的对象仍然是一个Component对象。
- Decorator模式并非解决“多子类衍生的多继承”问题,Decorator模式应用的要点在于解决“主体类在多个方向上的扩展功能”——是为“装饰”的含义。有点类似Bridge模式,但是Bridge桥接模式中的多维度的变化给人感觉一种“撕裂对象抽象”的感觉,通俗点讲就是将一个对象设计成这这样的接口不合适,设计成那样的接口也不合适,那么怎么办?分离!Decorator装饰模式中的功能扩展的变化不会撕裂接口(也就是说接口是稳定的),而是一级一级地向下(纵向)扩展接口,同时有叠加的效应(比如想同时获得多种功能扩展),这时候怎么办?横向扩展,也就是装饰!
在.net中Decorator模式的典型使用就是一系列的Stream类,作为实体类的有FileStream、NetworkStream、MemoryStream等,作为扩展功能接口的有BufferedStream、CryptoStream等。但是在Stream的扩展功能接口中是没有类似Decorator基类的,他是在每个功能类中自己保存一个Stream实体对象,在方法中直接调用Stream实体对象的方法。