程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> .NET實例教程 >> C#面向對象設計模式縱橫談 學習筆記10 Decorator 裝飾(結構型模式)

C#面向對象設計模式縱橫談 學習筆記10 Decorator 裝飾(結構型模式)

編輯:.NET實例教程

當我們在設計一個一個類繼承結構的時候,我們可能會存在以下的情況。首先有一種坦克的抽象類,那麼這個坦克會有很多中類型,那麼我們就派生這個坦克吧。比如說有德國虎式坦克,輕型坦克和工程坦克,那麼現在又有三種功能要擴展到這三種坦克上,如兩棲功能、夜視功能、雷達功能,可以把這三種功能設置為接口。按類繼承的設計方案,如果需要這三種坦克每種坦克都有相應的功能坦克,我們需要繼承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實體對象的方法。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved