設計模式筆記(10)---裝飾模式(結構型)

Gof定義

動態地給一個對象增加一些額外的職責。就增加功能而言,Decorator模式比生成子類更爲靈活。

首先來看一個小例子,假如我們需要給遊戲開發一種坦克,除了各種不同型號的坦克外,還希望在不同的場合來給坦克增加以下的一種或多種功能:比如紅外線夜視功能、水陸兩棲功能、GPS定位功能等。通常做法如下:

/// <summary>
/// 抽象坦克
/// </summary>
public abstract class Tank
{
    public abstract void Shot();
    public abstract void Run();
}

//型號
public class T50 : Tank { }
public class T60 : Tank { }
public class T70 : Tank { }

//在不同場合下的各種功能都抽象爲接口,
//如果某種型號的坦克需要某種功能就繼承該功能接口
//下面的IA IB IC 爲功能A B C的接口
/// <summary>
/// 有A功能的T50型號
/// </summary>
public class T50A : T50, IA { }
/// <summary>
/// 有A B兩種功能的T60型號
/// </summary>
public class T60AB : T60, IA, IB { }
/// <summary>
/// 有A B C三種功能的T70型號
/// </summary>
public class T70ABC : T70, IA, IB, IC { }

 

動機

上面描述的問題的根源在於我們“過多地使用了繼承來擴展對象的功能”,由於繼承爲賴幸引入了靜態特質,使得這種擴展方式缺乏靈活性,並且隨着子類的增多(擴展功能的增多),各種子類的組合(擴展功能的組合)會導致更多子類的膨脹(多繼承)。那麼如何使“對象功能的擴展”能夠根據需要來動態地實現,同時避免“擴展功能的增多”帶來的子類膨脹問題,從而使得任何“功能的變化”所導致的影響減爲最低呢?這就需要用到裝飾模式(Decorator)。下面先來看下裝飾模式的結構圖:

2009-11-29_211304

Component:對應上面例子中的Tank。

ConcreteComponent:對應坦克的型號 T50 T60 T70等。

DeCorator ConcreteDecoratorA ConcreteDecoratorB 這三個類就是接下來要實現了。

public abstract class Decorator : Tank
{
    private Tank _tank;
    public Decorator(Tank tank)
    {
        _tank = tank;
    }
    public override void Shot()
    {
        _tank.Shot();
    }
    public override void Run()
    {
        _tank.Run();
    }
}

public class ConcreteDecoratorA : Decorator
{
    public ConcreteDecoratorA(Tank tank):base(tank){}
    public override void Shot()
    {
        //擴展紅外功能
        base.Shot();
    }
    public override void Run()
    {
        base.Run();
    }
}

public class ConcreteDecoratorB : Decorator
{
    public ConcreteDecoratorB(Tank tank):base(tank){}
    public override void Shot()
    {
        //擴展兩棲功能
        base.Shot();
    }
    public override void Run()
    {
        base.Run();
    }
}

public class ConcreteDecoratorC : Decorator
{
    public ConcreteDecoratorC(Tank tank) : base(tank) { }
    public override void Shot()
    {
        //擴展定位功能
        base.Shot();
    }
    public override void Run()
    {
        base.Run();
    }
}

客戶端的調用

public class App
{
    static void Main()
    {
        Tank tank = new T50();
        //擁有紅外一種功能
        ConcreteDecoratorA da = new ConcreteDecoratorA(tank);
        //擁有紅外和兩棲兩種功能
        ConcreteDecoratorB db = new ConcreteDecoratorB(da);
        //擁有紅外、兩棲和定位三種功能
        ConcreteDecoratorC dc = new ConcreteDecoratorC(db);
        
        //需要什麼功能可以在此隨意組合,如果要擴展一種功能,只需要
        //添加一個具體的功能的類繼承Decorator即可
    }
}

 

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模式應用的要點在於解決“主體類在多個方向上的擴展功能”——是爲“裝飾”的含義。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章