設計模式之C#實現(三)FactoryMethod

設計模式之C#實現(三)FactoryMethod
作者:cuike519 更新時間: 2005-05-17  
 
     工廠方法的目的很明確就是定義一個用來創建對象的接口,但是他不直接創建對象,而由他的子類來創建,這樣一來就將創建對象的責任推遲到了該接口的子類中,創建什麼類型的對象由子類來決定,而創建對象的時間由接口來定。因此該模式可以在如下幾種情況下使用:1、a class can’t predict the class of objects it must create.2、a class wants its subclasses to specify the objects it creates.3、classes delegate responsibility to one of several helper subclasses,but do not know whick helper subclass is the delegate.在這裏我們舉一個麪包房的例子:麪包房的麪包機好比一個抽象產品,它是一般的麪包,那麼我們可以通過特殊的加工工藝使它變成不同種類的麪包比如:可以是中國式的麪包或者是美國的麪包等等。這些特殊種類的麪包就是我們的具體產品。而做麪包的人(也許是自己)就是Creator(創建者),我們使用我們的方法(工廠方法)來做我們想要的麪包。這裏的靈活性是不言而喻的,使用麪包機可以讓我們有更多的選擇,只有我們在需要的時候才決定吃那一種麪包,這不是很好的一件事情嗎,如果沒有面包機可能我們只能吃中國麪包了。下面我們在舉幾個我們非常熟悉的FCL(.NET Framrwork Class Labrarly)中的例子,IEnumerable和Ienumerator就是一個Creator和一個Product。另一個例子是:WebRequest 是 .NET Framework 的用於訪問 Internet 數據的請求/響應模型的抽象(在 Visual Basic 中爲 MustInherit)基類。使用該請求/響應模型的應用程序可以用協議不可知的方式從 Internet 請求數據。在這種方式下,應用程序處理 WebRequest 類的實例,而協議特定的子類則執行請求的具體細節。請求從應用程序發送到某個特定的 URI,如服務器上的 Web 頁。URI 從一個爲應用程序註冊的 WebRequest 子代列表中確定要創建的適當子類。註冊 WebRequest 子代通常是爲了處理某個特定的協議(如 HTTP 或 FTP),但是也可以註冊它以處理對特定服務器或服務器上的路徑的請求。(WebRequest摘自MSDN),這是一種帶參數的工廠方法。在FCL裏面工廠方法是一種最常見的模式應用。
  
  以上據了一個簡單的例子(麪包),不知道是不是貼切,我不想重新描寫一次該模式,但是再有必要的時候我會加以強調的,下面就用創建型模式的結構圖來展現這個模式。
  
  
   這次我想實現的就是書上的例子,首先我們需要看看我們的環境(上下文),我們爲了實現一個迷宮的創建環境所以,這個迷宮的簡單結構如下圖所示:
  
  
  爲了實現工廠模式來創建迷宮我們有一下的類圖,從圖中可以看出Creator(MazeGame)裏面有很多的工廠方法,用戶可以通過它來得到不同的房子(組成迷宮的組件),但是也許我們不知道我們的MazeGame是要創建什麼樣的Room所以我們可以定義一些子類(現在有兩個)用來實現不同的創建工作,Room是組成我們的迷宮的最常見的房子,MazeGame可以產生這樣大的房子但是爲了滿足不同的要求我們可以定義一些特殊的房子比如:帶炸彈的房子(是指牆上有炸彈)和可以施魔法的房子(可以對門施魔法)。這樣我們可以不改變原來的接口,用MazeGame的子類來完成這些特殊的創建工作,就像下面的圖中所表示的一樣。
  
  
   我們把創建的細節推遲到了子類去實現,這樣很大程度上提高了程序的韌性,如果我們想創建一個新的房子或者新的門我們可以在不改變原來代碼的情況下添加以個MazeGame子類來實現這個想法。但是我覺得這有一個問題爲了創建一個新的房子我們必須創建一個新的子類來滿足這個要求,這樣看起來好像是一對一對的平行的樣子。這樣會使我們的Creator的子類越來越多,我們可以使用帶參數的工廠方法減少這種類的繁殖,下面實現的代碼並沒有實現這種帶參數的工廠方法,網友們可以自己實現。我們只要給MakeWall和MakeDoor傳遞一個參數來確定創建什麼樣的Wall和Door,不過我們需要在默認的MazeGame裏相應的方法寫重裁版本。
  
   好了廢話就不多說了,GOF說的比我清楚多了(其實我也是從他們那裏學的J)。下面來實現我們的迷宮,代碼如下:
  
   首先實現我們的環境
  
  using System;
  
  using System.Collections;
  
  // 該命名空間中是一些運行實例德的環境包括Maze、Door等等
  
  namespace CommonObject{
  
  
  
   // 所有的迷宮構件的基類裏面有一個方法用來顯示當前構件的信息
  
   public class MapSite{
  
   public virtual string Enter(){
  
   return string.Empty;
  
   }
  
   }
  
  
  
   // 牆是組成迷宮的構件之一,這裏是一個很一般的牆(沒有炸彈)
  
   public class Wall : MapSite{
  
   public override string Enter(){
  
   return "This is a Wall.";
  
   }
  
   public Wall(){}
  
   }
  
  
  
   // 門也是迷宮的組成部分之一,這也是一個很普通的門(不能施魔法)
  
   public class Door : MapSite{
  
  
  
   public override string Enter(){
  
   return "This is a Door.";
  
   }
  
  
  
   // 門是在兩個房子之間的構件所以構造函數包含兩個房子
  
   // 說明是哪兩個房子之間的門
  
   public Door(Room roomFrom,Room roomTo){
  
   this.m_Room1 = roomFrom;
  
   this.m_Room2 = roomTo;
  
   }
  
  
  
   // 讓我們有機會可以從門進入另一個房子,將會得到
  
   // 另一個房子的引用
  
   public Room OtherSideFrom(Room roomFrom){
  
   if(this.m_Room1 == roomFrom)
  
   return this.m_Room2;
  
   else
  
   return this.m_Room1;
  
   }
  
  
  
   // 這是一些私有的變量
  
   private Room m_Room1;
  
   private Room m_Room2;
  
   // 描述門的狀態默認所有的新門都是關的
  
   private bool m_IsOpen = false;
  
   // 提供一個公共訪問的訪問器
  
   public bool IsOpen{
  
   set{this.m_IsOpen = value;}
  
   get{return this.m_IsOpen;}
  
   }
  
  
  
   }
  
  
  
   // 房間是組成迷宮的基本單位
  
   public class Room : MapSite
  
   {
  
   // 枚舉類型表示房子的面
  
   public enum Sides{
  
   North = 0,
  
   East,
  
   South,
  
   West
  
   }
  
  
  
   public override string Enter(){
  
   return "This is a Room.";
  
   }
  
  
  
   // 構造函數,爲了可以區分房間我們給每一個房間加一個標示
  
   public Room(int roomNumber){
  
   this.m_roomNumber = roomNumber;
  
   }
  
  
  
   // 設置房子的面,房子有4個面組成,因爲我們現在不知道每個面
  
   // 的具體類型(門?牆?)所以我們用MapSite類型。
  
   public void SetSide(Sides side,MapSite sideMap){
  
   this.m_side[(int)side] = sideMap;
  
   }
  
  
  
   // 得到指定的面,同樣我們不知道得到的是哪一個面
  
   // 所以我們用MapSite返回結構
  
   public MapSite GetSide(Sides side){
  
   return this.m_side[(int)side];
  
   }
  
  
  
   // 一些私有成員 房號
  
   protected int m_roomNumber;
  
   // 房子有4個面
  
   protected const int m_Sides = 4;
  
   // 用一個1維的MapSite數組存儲未知類型的面(牆或門)
  
   protected MapSite[] m_side = new MapSite[m_Sides];
  
  
  
   }
  
  
  
   // 帶炸彈的房子
  
   public class BombedRoom : Room{
  
   public BombedRoom(int roomNumber) : base(roomNumber){}
  
   }
  
  
  
   // 帶迷宮的房子
  
   public class EnchantedRoom : Room{
  
   public EnchantedRoom(int roomNumber,Spell spell) : base(roomNumber){}
  
   }
  
  
  
   // 這是一個特殊的牆--帶炸彈的
  
   public class BombedWall : Wall{}
  
  
  
   // 這是一個可以施魔法的門
  
   public class EnchantedDoor : Door{
  
   public EnchantedDoor(Room roomFrom,Room roomTo) : base(roomFrom,roomTo){}
  
   }
  
  
  
   // 這就是我們的迷宮了
  
   public class Maze{
  
   private ArrayList m_Rooms = new ArrayList();
  
   public Room RoomNumber(int roomNumber){
  
   return (Room)this.m_Rooms[roomNumber];
  
   }
  
   public void AddRoom(Room room){
  
   this.m_Rooms.Add(room);
  
   }
  
   }
  
  
  
   // 魔法類
  
   public class Spell{}
  
  }
  
  接下來是工廠方法的實現:
  
  using System;
  
  using System.Collections;
  
  namespace FactoryMethod_Example
  
  {
  
  
  
   // 加入MazeContext
  
   using CommonObject;
  
  
  
   // 實現了一些工廠方法的Creator類
  
   public class MazeGame
  
   {
  
   // 要返回給Client的對象
  
   private Maze m_Maze = null;
  
  
  
   // 一個訪問器用來得到maze
  
   public Maze Maze
  
   {
  
   get
  
   {
  
   if (this.m_Maze == null)
  
   this.m_Maze = CreateMaze();
  
   return this.m_Maze;
  
   }
  
   }
  
  
  
   // 構造器
  
   public MazeGame()
  
   {
  
   }
  
  
  
   // 以下就是一些工廠方法創建迷宮的每個個構件
  
   public virtual Maze MakeMaze()
  
   {
  
   return new Maze();
  
   }
  
  
  
   public virtual Room MakeRoom(int id)
  
   {
  
   return new Room(id);
  
   }
  
  
  
   public virtual Wall MakeWall()
  
   {
  
   return new Wall();
  
   }
  
  
  
   public virtual Door MakeDoor(Room room1, Room room2)
  
   {
  
   return new Door(room1, room2);
  
   }
  
  
  
   // 創建迷宮
  
   public Maze CreateMaze()
  
   {
  
   Maze maze = MakeMaze();
  
  
  
   // 創建門和房間
  
   Room room1 = MakeRoom(1);
  
   Room room2 = MakeRoom(2);
  
   Door theDoor = MakeDoor(room1, room2);
  
  
  
   // 將房間添加到迷宮裏面
  
   maze.AddRoom(room1);
  
   maze.AddRoom(room2);
  
  
  
   // 設置room1的面
  
   room1.SetSide(Room.Sides.North, MakeWall());
  
   room1.SetSide(Room.Sides.East, theDoor);
  
   room1.SetSide(Room.Sides.South, MakeWall());
  
   room1.SetSide(Room.Sides.West, MakeWall());
  
  
  
   // 設置room2的面
  
   room2.SetSide(Room.Sides.North, MakeWall());
  
   room2.SetSide(Room.Sides.East, MakeWall());
  
   room2.SetSide(Room.Sides.South, MakeWall());
  
   room2.SetSide(Room.Sides.West, theDoor);
  
  
  
   return maze;
  
   }
  
   }
  
  
  
   // 創建帶炸彈迷宮
  
   public class BombedMazeGame : MazeGame{
  
   public BombedMazeGame(){}
  
   public override Wall MakeWall(){
  
   return new BombedWall();
  
   }
  
  
  
   public override Room MakeRoom(int n){
  
   return new BombedRoom(n);
  
   }
  
   }
  
  
  
   // 創建帶魔法的迷宮
  
   public class EnchantedMazeGame : MazeGame{
  
  
  
   private Spell m_spell;
  
  
  
   public EnchantedMazeGame(Spell spell){
  
   this.m_spell = spell;
  
   }
  
   public override Door MakeDoor(Room r1,Room r2){
  
   return new EnchantedDoor(r1,r2);
  
   }
  
  
  
   public override Room MakeRoom(int n){
  
   return new EnchantedRoom(n,this.m_spell);
  
   }
  
  
  
   }
  
  
  
  }
  
   上面的代碼在.NET IDE裏測試通過,上面說過了在FCL裏面又很多的工廠方法的實現,如果想要繼續學習和理解可以看看MSDN的幫助。希望我寫的這些可以幫助你更好的理解工廠方法模式。如果有什麼不正確的地方希望指出可以發郵件到[email protected]裏,我將虛心學習,希望我們可以共同進步。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章