1. 程式人生 > >設計模式之C#實現--FactoryMethod

設計模式之C#實現--FactoryMethod

工廠方法的目的很明確就是定義一個用來建立物件的介面,但是他不直接建立物件,而由他的子類來建立,這樣一來就將建立物件的責任推遲到了該介面的子類中,建立什麼型別的物件由子類來決定,而建立物件的時間由介面來定。因此該模式可以在如下幾種情況下使用: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; // 該名稱空間中是一些執行例項德的環境包括MazeDoor等等 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的幫助。希望我寫的這些可以幫助你更好的理解工廠方法模式。如果有什麼不正確的地方希望指出可以發郵件到裡,我將虛心學習,希望我們可以共同進步。