Design Patterns: Solidify Your C# Application Architecture with Design Patterns中文版(中篇) (轉)

worldblog發表於2007-12-11
Design Patterns: Solidify Your C# Application Architecture with Design Patterns中文版(中篇) (轉)[@more@]

Design Patterns: Solidify Your Application Architecture with Design Patterns中文版(中篇):namespace prefix = o ns = "urn:schemas--com::office" />

作者:Samir Bajaj

譯者:榮耀

【譯序:C#進階文章。譯者對Samir提供的C#例子進行了簡單整理(作者提供的某些程式碼在譯者的環境中無法透過編譯),並編寫了對應的C++示例,一併置於譯註中,以便讀者比對。譯文中所有C#、C++環境均為Microsoft 7.0 Beta2】

decorator

  客戶應用常常需要加強某些類的方法所提供的服務。可能需要分別在方法前後插入一些預先處理和後繼處理的程式碼。要實現這個目標,一種辦法是乾脆做成不同的方法呼叫。然而,這種方式不但麻煩,而且不利於的擴充套件。例如,如果對於不同的客戶來說明顯要不同的預先處理和後繼處理任務,應用程式邏輯將會因為條件語句而變得晦澀不清並難以維護。問題是如何才能增強類所提供的功能的同時又不影響客戶程式碼,而Decorator正是所需。

  讓我們來考察一個具有傳輸功能的類的例子。這樣的類程式碼可能如表5所示。

表5

class FileTraner

{

  public virtual void (string url, byte[] data, int size)

  {

  // 檔案

  }

  public virtual void (string url, byte[] data, int size)

  {

  // 檔案

  }

}

  假定有一個客戶程式對這個功能感興趣。除了能夠上傳和下載檔案外,客戶應用還希望能夠將所有的請求和執行訪問檢查寫入日誌。基於decorator模式的一種實現方式是從FileTransfer類派生出一個類並過載虛方法,並於呼叫基類方法之前或之後,插入附加程式碼。如表6所示。

表6

class Decorator : FileTransfer

{

  private FileTransfer ft = new FileTransfer();

  private bool IsAccessAllowed(string url)

  {

  bool result = true;

  // 決定是否對請求的URL訪問授權

  return result;

  }

  private void LogAccess(string url)

  {

  // 將URL、時間、身份等資訊寫入

  Console.WriteLine("Logging access to {0}", url);

  }

  public overr void Download(string url, byte[] data, int size)

  {

  if (!IsAccessAllowed(url))

  return;

  ft.Download(url, data, size);

  LogAccess(url);

  } 

}

  客戶程式可以繼續使用同樣的介面【譯註:並非C#語義的介面】進行工作。實際上,還可以進一步改進這個方案,可將FileTransfer類和Decorator類改為實現同一個具有Upload和Download方法的介面的類。如此,就可以使客戶程式只依照介面工作,而同具體實現徹底解耦。

  當一定範圍的擴充套件和任務可以在現有類的基礎上進行,並且將所有擴充套件都定義為類是不切實際的情況下,使用decorator模式可以動態、透明地新增或移去功能而不會影響客戶程式碼。

【譯註:以下是decorator模式完整示例

C#示例:

using System;

class FileTransfer

{

  public virtual void Download(string url, byte[] data, int size)

  {

  // 下載檔案

  }

  public virtual void Upload(string url, byte[] data, int size)

  {

  // 上傳檔案

}

}

class Decorator : FileTransfer

{

  private FileTransfer ft = new FileTransfer();

  private bool IsAccessAllowed(string url)

  {

  bool result = true;

  // 決定是否對請求的URL訪問授權

  return result;

  }

  private void LogAccess(string url)

  {

  // 將URL、時間、使用者身份等資訊寫入資料庫

    Console.WriteLine("Logging access to {0}", url);

  }

  public override void Download(string url, byte[] data, int size)

  {

  if (!IsAccessAllowed(url)) return;

    ft.Download(url, data, size);

    LogAccess(url);

  }

  public override void Upload(string url, byte[] data, int size)

  {

  if (!IsAccessAllowed(url)) return;

    ft.Upload(url, data, size);

    LogAccess(url);

  }

}

class Application

{

  public static void Main()

  {

    Console.Write("Enter URL to access: ");

  string url = Console.ReadLine();

    Console.Write("Enable logging and access check? ");

  string input = Console.ReadLine();

  char ch = char.Parse(input);

  bool decoration = (ch == 'y' || ch == 'Y');

    FileTransfer ft = null;

  if (!decoration)

  ft = new FileTransfer();

  else

  ft = new Decorator(); 

  byte[] buf = new byte[1024];

    ft.Download(url, buf, 1024);

  }

}

/*以下是某次執行時輸出結果:

Enter URL to access:

Enable logging and access check? Y

Logging access to

*/

C++示例:【譯註:下例中之所以混用std::string和byte陣列只是為了便於和C#程式比對而已】

#include "stdafx.h";

#include

#include

using namespace std;

typedef unsigned char byte;

class FileTransfer

{

public:

  virtual void Download(string url, byte data[], int size)

  {

  // 下載檔案

  }

  virtual void Upload(string url, byte data[], int size)

  {

  // 上傳檔案

  }

};

class Decorator : public FileTransfer  // decorated file transfer

{

private:

  FileTransfer* ft;

  bool IsAccessAllowed(string url)

  {

  bool result = true;

  // 決定是否對請求的URL訪問授權

  return result;

  }

  void LogAccess(string url)

  {

  // 將URL、時間、使用者身份等資訊寫入資料庫

    cout<

  }

public:

  Decorator()

  {

  ft = new FileTransfer();

  }

  ~Decorator()

  {

  if (ft)

  {

    delete ft;

  ft = NULL;

  }

  }

  void Download(string url, byte data[], int size)

  {

  if (!IsAccessAllowed(url)) return;

    ft->Download(url, data, size);

    LogAccess(url);

  }

  void Upload(string url, byte data[], int size)

  {

  if (!IsAccessAllowed(url)) return;

    ft->Upload(url, data, size);

    LogAccess(url);

  }

};

int _tmain(int argc, _TCHAR* argv[])

{

  cout<

  string url;

  cin>>url;

  cout<

  char ch;

  cin>>ch;

  bool decoration = (ch == 'y' || ch == 'Y');

  FileTransfer* ft = NULL;

  if (!decoration)

  ft = new FileTransfer();

  else

  ft = new Decorator(); 

  byte* buf = new byte[1024];

  ft->Download(url, buf, 1024);

  delete []buf;

  if (ft != NULL)

  {

  delete ft;

  ft = NULL;

  }

  return 0;

}

/*以下是某次執行時輸出結果:

Enter URL to access:

Enable logging and access check? Y

Logging access to

*/

composite

  當需要以一致的方式處理聚集和個體物件時,composite模式就派上了用場。【譯註:此“聚集”並非COM語義的聚集】一個常見的例子是列舉資料夾內容。資料夾可能不單包括檔案,也可能有子資料夾。遞迴列舉某個頂層資料夾的應用可以使用條件語句來區分檔案和子資料夾,並可透過遍歷目錄樹來列印出子資料夾中的所有檔案。對該問題的一個更好的解決方案是使用composite模式。使用這種方式,資料夾內的每一種專案,不管是檔案、子資料夾、印表機或任何一種目錄元素的別名,都是遵從同樣介面的某個類的例項,該介面提供某個方法來描述這些元素的使用者友好的名稱。如此,客戶應用就不必區分每一種不同的元素,這也降低了應用邏輯的複雜性。

  另一個例子,也是我下面要用C#語言展現的,是一個畫圖應用。它從物件資料庫中提取基本的和組合的圖形元素,並將它們畫在畫布上。假定資料庫可以容納Line、Circle和Drawing(包容有Line和Circle)。讓我們看看如表7所示的介面。

表7

interface Shape

{

  void Draw();

}

  介面Shape有一個方法Draw。諸如Line之類的簡單圖形物件可以實現該介面,過載方法Draw【譯註:對於C#中的interface及其實現,並不需要virtual或override關鍵字,故與其說是“過載”,不如說是實現】,以在畫布上畫線。參見表8。

表8

class Line : Shape

{

  private double x1, y1, x2, y2;

  public Line(double x1, double y1, double x2, double y2)

  {

  this.x1 = x1; this.y1 = y1;

  this.x2 = x2; this.y2 = y2;

  }

  public void Draw()

  {

  // 從(x1, y1) 到(x2, y2)畫一條線

  }

}

  Circle類和Line類類似。為了能夠一致地處理聚集類和簡單實體類,聚集物件也應該實現Shape介面。Drawing是圖形物件的集合類,它實現了Draw方法,列舉出其容納的所有基本圖形物件並將它們一一畫出。表9的程式碼展示了其工作原理。

表9

class Drawing : Shape

{

  private ArrayList shapes;

  public Drawing()

  {

  shapes = new ArrayList();

  }

  public void Add(Shape s)

  {

  shapes.Add(s);

  }

  public void Draw()

  {

  IEnumerator enumerator = shapes.GetEnumerator();

  while (enumerator.MoveNext())

  ((Shape) enumerator.Current).Draw();

  }

}

  注意,客戶無需關心某個圖形物件屬於基本型別和還是集合型別。橫跨於它們之上的公共介面【譯註:此處即是Shape】使得我們可以向其中新增新物件【譯註:新型別的物件】而不會對客戶程式產生任何影響。

  透過公共介面,composite模式避免了客戶程式必須區分個體物件和容器物件,這在相當大的程度上促進了程式碼重用並簡化了客戶程式邏輯。

  注意,Drawing類的Draw方法使用了System.Collections名字空間中的類。如欲瞭解類庫更多知識,請參閱.NET SDK的有關文件。

【譯註:以下是composite模式完整示例

C#示例:

using System;

using System.Collections;

interface Shape

{

  void Draw();

}

class Line : Shape

{

  private double x1, y1, x2, y2;

  public Line(double x1, double y1, double x2, double y2)

  {

    this.x1 = x1;

    this.y1 = y1;

    this.x2 = x2;

    this.y2 = y2;

  }

  public void Draw()

  {

  //從(x1, y1) 到(x2, y2)畫一條線

    Console.WriteLine("Drawing a line");

  }

}

class Circle : Shape

{

  private double x, y, r;

  public Circle(double x, double y, double )

  {

  this.x = x;

  this.y = y;

  this.r = r;

  }

  public void Draw()

  {

  //以(x, y)為圓心,r為半徑畫一個圓

    Console.WriteLine("Drawing a circle");

  }

}

class Drawing : Shape

{

  private ArrayList shapes;

  public Drawing()

  {

  shapes = new ArrayList();

  }

  public void Add(Shape s)

  {

    shapes.Add(s);

  }

  public void Draw()

  {

    IEnumerator enumerator = shapes.GetEnumerator();

  while (enumerator.MoveNext())

    ((Shape) enumerator.Current).Draw();

  }

}

class Application

{

  public static void Main()

  {

  Shape[] array = new Shape[3];

    array[0] = new Line(0, 0, 10, 12);

    array[1] = new Circle(2, 3, 5.5); 

    Drawing dwg = new Drawing();

    dwg.Add(new Line(3, 4, 3, 5));

    dwg.Add(new Circle(5, 6, 7.7));

    array[2] = dwg;

  // 畫出所有的圖形,注意:用一致的方式來訪問所有物件

  for (int i = 0; i < 3; ++i)

    array[i].Draw();

  }

}

/*以下是程式輸出結果:

Drawing a line

Drawing a circle

Drawing a line

Drawing a circle

*/


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-991478/,如需轉載,請註明出處,否則將追究法律責任。

相關文章