C#面向对象设计模式纵横谈 学习笔记10 Decorator 装饰(结构型模式)

当我们在设计一个一个类继承结构的时候,我们可能会存在以下的情况。首先有一种坦克的抽象类,那么这个坦克会有很多中类型,那么我们就派生这个坦克吧。比如说有德国虎式坦克,轻型坦克和工程坦克,那么现在又有三种功能要扩展到这三种坦克上,如两栖功能、夜视功能、雷达功能,可以把这三种功能设置为接口。按类继承的设计方案,如果需要这三种坦克每种坦克都有相应的功能坦克,我们需要继承Tank类,并实现相应功能的接口,那么我们的扩展类就有9种,这还不包括功能的组合,那么现在问题就出来了,这种类继承的模式会导致子类无限制的增加,这是我们无法忍受的。那么怎么解决这个问题呢?我们就需要Decorator模式来解决。

Decorator模式的动机:

上述描述的问题根源在于我们“过度地使用了继承来扩展对象的功能”,由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀(多继承)。如何使“对象功能的扩展”能够根据需要来动态地实现?同时避免“扩展功能的增多”带来的子类膨胀问题?从而使得任何“功能扩展变化”所导致的影响将为最低?

Decorator模式的意图:

动态地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类更为灵活。

如何用代码来实现Decorator模式:

首先我们有一个抽象基类Tank,里面有两个抽象方法Shot和Run

public   abstract   class  Tank
{
    
public abstract void Shot();
    
public abstract void Run();
}

我们有三个派生类来表明三种坦克的类型,并实现了Shot和Run方法

public   class  TigerTank : Tank
{
    
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()
    
{
    }

}
现在,我们按照往常的设计方法应该写功能接口interface了,但是功能接口会带来类的膨胀,这不是我们要的目的,那么我们看下面的代码式如何做的。
public   abstract   class  Decorator : Tank  // 这里是一个接口继承
{
    
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   class  Amphibian : Decorator
{
    
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类

如何来使用相关的功能呢?

static   void  Main( string [] args)
{
    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实体对象的方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值