国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

.NET設計模式研究之裝飾模式

2019-11-17 04:53:22
字體:
來源:轉載
供稿:網友
概述

  在軟件系統中,有時候我們會使用繼續來擴展對象的功能,但是由于繼續為類型引入的靜態特質,使得這種擴展方式缺乏靈活性;并且隨著子類的增多(擴展功能的增多),各種子類的組合(擴展功能的組合)會導致更多子類的膨脹。如何使“對象功能的擴展”能夠根據需要來動態地實現?同時避免“擴展功能的增多”帶來的子類膨脹問題?從而使得任何“功能擴展變化”所導致的影響將為最低?這就是本文要講的Decorator模式。

  意圖

  動態地給一個對象添加一些額外的職責。就增加功能來說,Decorator模式相比生成子類更為靈活。[GOF 《設計模式》]

  結構圖

.NET設計模式研究之裝飾模式(圖一)

圖1 Decorator模式結構圖

  生活中的例子

  裝飾模式動態地給一個對象添加額外的職責。不論一幅畫有沒有畫框都可以掛在墻上,但是通常都是有畫框的,并且實際上是畫框被掛在墻上。在掛在墻上之前,畫可以被蒙上玻璃,裝到框子里;這時畫、玻璃和畫框形成了一個物體。

.NET設計模式研究之裝飾模式(圖二)

圖2 使用有畫框的畫作為例子的裝飾模式對象圖

  裝飾模式解說

  在軟件開發中,經常會碰到動態地為一個對象而不是整個類增加一些功能的問題,還是以我慣用的記錄日志的例子來說明吧(也許在Decorator模式里面用這個例子不是非凡合適)。現在要求我們開發的記錄日志的組件,除了要支持數據庫記錄DatabaseLog和文本文件記錄TextFileLog兩種方式外,我們還需要在不同的應用環境中增加一些額外的功能,比如需要記錄日志信息的錯誤嚴重級別,需要記錄日志信息的優先級別,還有日志信息的擴展屬性等功能。在這里,假如我們不去考慮設計模式,解決問題的方法其實很簡單,可以通過繼續機制去實現,日志類結構圖如下:

.NET設計模式研究之裝飾模式(圖三)

圖3

  實現代碼如下:

public abstract class Log
{
 public abstract void Write(string log);
}

public class DatabaseLog : Log
{
 public override void Write(string log)
 {
  //......記錄到數據庫中
 }
}

public class TextFileLog : Log
{
 public override void Write(string log)
 {
  //......記錄到文本文件中
 }
}
  需要記錄日志信息的錯誤嚴重級別功能和記錄日志信息優先級別的功能,只要在原來子類DatabaseLog和TextFileLog的基礎上再生成子類即可,同時需要引進兩個新的接口IError和I PRiority,類結構圖如下:

.NET設計模式研究之裝飾模式(圖四)
(點擊查看原圖)

圖4

  實現代碼如下:

public interface IError
{
 void SetError();
}

public interface ipriority
{
 void SetPriority();
}

public class DBErrorLog : DatabaseLog, IError
{
 public override void Write(string log)
 {
  base.Write(log);
 }
 public void SetError()
 {
  //......功能擴展,實現了記錄錯誤嚴重級別
 }
}

public class DBPriorityLog : DatabaseLog, IPriority
{
 public override void Write(string log)
 {
  base.Write(log);
 }
 public void SetPriority()
 {
  //......功能擴展,實現了記錄優先級別
 }
}

public class TFErrorLog : TextFileLog, IError
{
 public override void Write(string log)
 {
  base.Write(log);
 }
 public void SetError()
 {
  //......功能擴展,實現了記錄錯誤嚴重級別
 }
}

public class TFPriorityLog : TextFileLog, IPriority
{
 public override void Write(string log)
 {
  base.Write(log);
 }
 public void SetPriority()
 {
  //......功能擴展,實現了記錄優先級別
 }
}
  此時可以看到,假如需要相應的功能,直接使用這些子類就可以了。這里我們采用了類的繼續方式來解決了對象功能的擴展問題,這種方式是可以達到我們預期的目的。然而,它卻帶來了一系列的問題。首先,前面的分析只是進行了一種功能的擴展,假如既需要記錄錯誤嚴重級別,又需要記錄優先級時,子類就需要進行接口的多重繼續,這在某些情況下會違反類的單一職責原則,注重下圖中的藍色區域:

.NET設計模式研究之裝飾模式(圖五)
(點擊查看原圖)

圖5

  實現代碼:


public class DBEPLog : DatabaseLog, IError, IPriority
{
 public override void Write(string log)
 {
  SetError();
  SetPriority();
  base.Write(log);
 }
 public void SetError()
 {
  //......功能擴展,實現了記錄錯誤嚴重級別
 }
 public void SetPriority()
 {
  //......功能擴展,實現了記錄優先級別
 }
}

public class TFEPLog : DatabaseLog, IError, IPriority
{
 public override void Write(string log)
 {
  SetError();
  SetPriority();
  base.Write(log);
 }
 public void SetError()
 {
  //......功能擴展,實現了記錄錯誤嚴重級別
 }
 public void SetPriority()
 {
  //......功能擴展,實現了記錄優先級別
 }
}
  其次,隨著以后擴展功能的增多,子類會迅速的膨脹,可以看到,子類的出現其實是DatabaseLog和TextFileLog兩個子類與新增加的接口的一種排列組合關系,所以類結構會變得很復雜而難以維護,正如象李建忠老師說的那樣“子類復子類,子類何其多”;最后,這種方式的擴展是一種靜態的擴展方式,并沒有能夠真正實現擴展功能的動態添加,客戶程序不能選擇添加擴展功能的方式和時機。

  現在又該是Decorator模式出場的時候了,解決方案是把Log對象嵌入到另一個對象中,由這個對象來擴展功能。首先我們要定義一個抽象的包裝類LogWrapper,讓它繼續于Log類,結構圖如下:

.NET設計模式研究之裝飾模式(圖六)

圖6

  實現代碼如下:

public abstract class LogWrapper : Log
{
 private Log _log;
 public LogWrapper(Log log)
 {
  _log = log;
 }
 public override void Write(string log)
 {
  _log.Write(log);
 }
}
  現在對于每個擴展的功能,都增加一個包裝類的子類,讓它們來實現具體的擴展功能,如下圖中綠色的區域:

.NET設計模式研究之裝飾模式(圖七)

圖7

  實現代碼如下:

public class LogErrorWrapper : LogWrapper
{
 public LogErrorWrapper(Log _log)
 :base(_log){}

 public override void Write(string log)
 {
  SetError(); //......功能擴展
  base.Write(log);
 }

 public void SetError()
 {
  //......實現了記錄錯誤嚴重級別
 }
}

public class LogPriorityWrapper : LogWrapper
{
 public LogPriorityWrapper(Log _log): base(_log){}

 public override void Write(string log)
 {
  SetPriority(); //......功能擴展
  base.Write(log);
 }

 public void SetPriority()
 {
  //......實現了記錄優先級別
 }
}
  到這里,LogErrorWrapper類和LogPriorityWrapper類真正實現了對錯誤嚴重級別和優先級別的功能的擴展。我們來看一下客戶程序如何去調用它:

public class Program
{
 public static void Main(string[] args)
 {
  Log log = new DatabaseLog();
  LogWrapper lew1 = new LogErrorWrapper(log);

  //擴展了記錄錯誤嚴重級別

  lew1.Write( "Log Message");
  LogPriorityWrapper lpw1 = new LogPriorityWrapper(log);

  //擴展了記錄優先級別

  lpw1.Write( "Log Message");
  LogWrapper lew2 = new LogErrorWrapper(log);
  LogPriorityWrapper lpw2 = new LogPriorityWrapper(lew2); //這里是lew2

  //同時擴展了錯誤嚴重級別和優先級別

  lpw2.Write( "Log Message");
 }
}
  注重在上面程序中的第三段裝飾才真正體現出了Decorator模式的精妙所在,這里總共包裝了兩次:第一次對log對象進行錯誤嚴重級別的裝飾,變成了lew2對象,第二次再對lew2對象進行裝飾,于是變成了lpw2對象,此時的lpw2對象同時擴展了錯誤嚴重級別和優先級別的功能。也就是說我們需要哪些功能,就可以這樣繼續包裝下去。到這里也許有人會說LogPriorityWrapper 類的構造函數接收的是一個Log對象,為什么這里可以傳入LogErrorWrapper對象呢?通過類結構圖就能發現,LogErrorWrapper類其實也是Log類的一個子類。

  我們分析一下這樣會帶來什么好處?首先對于擴展功能已經實現了真正的動態增加,只在需要某種功能的時候才進行包裝;其次,假如再出現一種新的擴展功能,只需要增加一個對應的包裝子類(注重:這一點任何時候都是避免不了的),而無需再進行很多子類的繼續,不會出現子類的膨脹,同時Decorator模式也很好的符合了面向對象設計原則中的“優先使用對象組合而非繼續”和“開放-封閉”原則。.NET 中的裝飾模式

  1..NET中Decorator模式一個典型的運用就是關于Stream,它存在著如下的類結構:

.NET設計模式研究之裝飾模式(圖八)
(點擊查看原圖)

圖8

  可以看到, BufferedStream和CryptoStream其實就是兩個包裝類,這里的Decorator模式省略了抽象裝飾角色(Decorator),示例代碼如下:


class Program
{
 public static void Main(string[] args)
 {
  MemoryStream ms =new MemoryStream(new byte[] { 100,456,864,222,567});

  //擴展了緩沖的功能

  BufferedStream buff = new BufferedStream(ms);

  //擴展了緩沖,加密的功能

  CryptoStream crypto = new CryptoStream(buff);
 }
}
  通過反編譯,可以看到BufferedStream類的代碼(只列出部分),它是繼續于Stream類:

public sealed class BufferedStream : Stream
{
 // Methods

 private BufferedStream();
 public BufferedStream(Stream stream);
 public BufferedStream(Stream stream, int bufferSize);
 // Fields

 private int _bufferSize;
 private Stream _s;
}

  2.在Enterprise Library中的DAAB中有一個DbCommandWrapper的包裝類,它實現了對IDbCommand類的包裝并提供了參數處理的功能。結構圖如下:

.NET設計模式研究之裝飾模式(圖九)
(點擊查看原圖)

圖9

  示意性代碼如下:

public abstract class DBCommandWrapper : MarshalByRefObject, IDisposable
{}

public class SqlCommandWrapper : DBCommandWrapper
{}

public class OracleCommandWrapper : DBCommandWrapper
{}

  效果及實現要點

  1.Component類在Decorator模式中充當抽象接口的角色,不應該去實現具體的行為。而且Decorator類對于Component類應該透明,換言之Component類無需知道Decorator類,Decorator類是從外部來擴展Component類的功能。

  2.Decorator類在接口上表現為is-a Component的繼續關系,即Decorator類繼續了Component類所具有的接口。但在實現上又表現為has-a Component的組合關系,即Decorator類又使用了另外一個Component類。我們可以使用一個或者多個Decorator對象來“裝飾”一個Component對象,且裝飾后的對象仍然是一個Component對象。

  3.Decortor模式并非解決“多子類衍生的多繼續”問題,Decorator模式的應用要點在于解決“主體類在多個方向上的擴展功能”——是為“裝飾”的含義。

  4.對于Decorator模式在實際中的運用可以很靈活。 假如只有一個ConcreteComponent類而沒有抽象的Component類,那么Decorator類可以是ConcreteComponent的一個子類。

.NET設計模式研究之裝飾模式(圖十)

圖10

  假如只有一個ConcreteDecorator類,那么就沒有必要建立一個單獨的Decorator類,而可以把Decorator和ConcreteDecorator的責任合并成一個類。

.NET設計模式研究之裝飾模式(圖十)

圖11

  5.Decorator模式的優點是提供了比繼續更加靈活的擴展, 通過使用不同的具體裝飾類以及這些裝飾類的排列組合,可以創造出很多不同行為的組合。

  6.由于使用裝飾模式,可以比使用繼續關系需要較少數目的類。使用較少的類,當然使設計比較易于進行。但是,在另一方面,使用裝飾模式會產生比使用繼續關系更多的對象。更多的對象會使得查錯變得困難,非凡是這些對象看上去都很相像。

  適用性

  在以下情況下應當使用裝飾模式:

  1.需要擴展一個類的功能,或給一個類增加附加責任。

  2.需要動態地給一個對象增加功能,這些功能可以再動態地撤銷。

  3.需要增加由一些基本功能的排列組合而產生的非常大量的功能,從而使繼續關系變得不現實。

  總結

  Decorator模式采用對象組合而非繼續的手法,實現了在運行時動態的擴展對象功能的能力,而且可以根據需要擴展多個功能,避免了單獨使用繼續帶來的“靈活性差”和“多子類衍生問題”。同時它很好地符合面向對象設計原則中“優先使用對象組合而非繼續”和“開放-封閉”原則。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 沁阳市| 淮滨县| 太白县| 康保县| 克拉玛依市| 弋阳县| 惠水县| 启东市| 峨山| 渝北区| 海城市| 昌邑市| 越西县| 永胜县| 辉南县| 涞源县| 玛多县| 敦煌市| 呈贡县| 中牟县| 虎林市| 德清县| 库尔勒市| 神农架林区| 微博| 始兴县| 敦煌市| 方正县| 营山县| 恩平市| 青州市| 启东市| 赞皇县| 嘉兴市| 明溪县| 抚远县| 上饶县| 攀枝花市| 云浮市| 云浮市| 文昌市|