2011-10-30 70 views
2

使用Factory的哪種方式更好(正確)?使用工廠模式

IPacket info = PacketFactory.CreatePacketObject(PacketType.Info, currentUser, DateTime.Now, " disconnected"); 

還是應該在PacketFactory中拋出第二種方法並使用它?

IPacket info = PacketFactory.CreatePacketObject(PacketType.Info); 
      info.CreationTime = DateTime.Now; 
      info.Creator = currentUser; 
      info.Data = " disconnected"; 

或者可能是其他的?

PacketFactory代碼:

public static class PacketFactory 
    { 
     public static IPacket CreatePacketObject(PacketType type) 
     { 
      IPacket packetToCreate = null; 
      switch (type) 
      { 
       case PacketType.Info: 
        packetToCreate = new Info(); 
        break; 
       case PacketType.Log: 
        packetToCreate = new Log(); 
        break; 
       case PacketType.Message: 
        packetToCreate = new Message(); 
        break; 
      } 
      return packetToCreate; 
     } 

     public static IPacket CreatePacketObject(PacketType type, Client creator, DateTime creationTime, string data) 
     { 
      IPacket packetToCreate = null; 
      switch (type) 
      { 
       case PacketType.Info: 
        packetToCreate = new Info(creator, creationTime, data); 
        break; 
       case PacketType.Log: 
        packetToCreate = new Log(creator, creationTime, data); 
        break; 
       case PacketType.Message: 
        packetToCreate = new Message(creator, creationTime, data); 
        break; 
      } 
      return packetToCreate; 
     } 

    } 
+0

沒有上下文,很難說。在你的簡單例子中,我根本不會使用工廠。 – CodesInChaos

+0

如果您在生產代碼中投射工廠的結果(而不僅僅是一個例子),那是一種代碼味道。僅通過界面參考工廠的結果。 – TrueWill

+0

@TrueWill ok,現在在這種情況下是什麼? – Saint

回答

7

在應用模式之前,您應該清楚自己從中獲益的方式,在這種情況下,我並沒有真正看到引入靜態「工廠」會爲您帶來什麼。從客戶端PacketFactory的角度來看它:已經介紹它減少了客戶端與各種具體實現器之間的耦合?我認爲不是,因爲客戶必須通過指定PacketType.Info,PacketType.MessagePacketType.Log的枚舉值來知道它想要哪種類型的IPacket。與客戶瞭解Info,MessageLog類有什麼不同?由於「Factory」是一個靜態類,因此客戶端與返回的IPacket類型耦合,因爲它只是調用相應的實現者的構造函數,因爲您必須更改客戶端才能使其兩種情況下都可以使用不同類型的IPacket。因此,如果您確實需要使用某種工廠,那麼我會建議使用抽象工廠模式,以便工廠的客戶只依賴工廠界面,因此能夠使用不同種類的工廠IPacket而不必更改。例如:

public interface IPacketFactory 
{ 
    IPacket CreatePacket(); 
    IPacket CreatePacket(Client creator, DateTime creationTime, string data); 
} 

public class MessageFactory : IPacketFactory 
{ 
    public CreatePacket() 
    { 
     return new Message(); 
    } 

    public CreatePacket(Client creator, DateTime creationTime, string data) 
    { 
     return new Message(creator, creationTime, data); 
    } 
} 

//You'd implement factories for each IPacket type... 

public class Client 
{ 
    private IPacketFactory _factory; 

    public Client(IPacketFactory factory) 
    { 
     _factory = factory; 
    } 

    public SomeMethodThatNeedsToCreateIPacketInstance() 
    { 
     IPacket packet = _factory.CreatePacket(); 

    //work with packet without caring what type it is 
    } 

} 


//a higher level class or IOC container would construct the client with the appropriate factory 

Client client = new Client(new MessageFactory()); 

// the Client class can work with different IPacket instances without it having to change (it's decoupled) 

Client client2 = new Client(new LogFactory()); 

至於廠家是否應該允許人們構建IPacket沒有指定創建者,數據和創建時間或不依賴於類的變量。如果在不指定字段時可以滿足類不變量,那麼很好,否則它們應該是必需的。班級工作的一部分應該是確保它不能構成無效狀態,因爲班級的用戶將視情況而定。

在該IPacket實施者之一需要額外的參數:

抽象工廠模式存在需要對所有實現一個統一的接口,因此,如果它是有道理的,所有的工廠有一個用額外的參數創建方法,然後你可以將它們添加到接口。其中一種形式是傳遞具有Create方法可用於導出其所需的額外參數值的各種屬性/方法的對象。特殊情況是Double Dispatch,其中調用者將自身傳遞給客戶端(在本例中爲客戶端),然後從Create方法中調用。

//in MessageFactory : the PacketContext holds various data that may be relevant to creation 

public IPacket Create(Client creator, DateTime creationTime, string data, PacketContext ctx) 
{ 
    return new Message(creator, creationTime, data, ctx.SomeExtraData); 
} 

//in LogFactory: the Log doesn't need anything from the PacketContext but it does call something on the Client (Double Dispatch) 

public IPacket Create(Client creator, DateTime creationTime, string data, PacketContext ctx) 
{ 
    return new Log(creator.Name, creationTime, data); 
} 

你要記住,我們的目標是抽象正在創建的IPacket類型,因此,如果同時實施這種方法,你開始得到了Client已開始暗中知道具體的類型正在興建然後感覺你可能不得不退後一步,考慮工廠是否合適。您唯一的選擇是在構建工廠時提供額外的信息(即將其傳遞給構造函數)。

public class MessageFactory : IPacketFactory 
{ 
    private object _data; 

    public MessageFactory(object extraData) 
    { 
     _data = extraData; 
    } 

    IPacket CreatePacket(Client creator, DateTime creationTime, string data) 
    { 
     return new Message(creator, creationTime, data, _extraData); 
    } 

    ///rest of implementation 
} 

這些代表的一些選項,但在任何情況下,我會強烈建議您不要使用靜態或單「工廠」類,因爲它會強夫婦您的客戶端類的工廠,最可能是IPacket的子類。

+0

不錯的解決方案,但如果我想使用一些其他的構造函數,例如在MessageFactory中有兩個額外的參數。我應該如何構建這些工廠? – Saint

+0

@Saint - 添加了新的部分。 HTH。 –

+0

偉大的答案!它幫助我瞭解工廠模式 – MUG4N

3

IMO這取決於創建時間,創建者和數據是否需要創建一個數據包的有效實例。如果是的話,我會堅持使用解決方案之一,並要求儘早設置這些屬性,在您的工廠方法中就是這樣。如果這些屬性不應該在稍後時間改變,那麼我會另外使這些屬性只讀。 如果設置屬性是可選的,請保持您的工廠界面乾淨並刪除具有屬性的過載。

1

我建議第一種方法監守這樣

  • 您可以標記所有IPacket屬性爲只讀,所有實例都將是不可變的
  • 這是很清楚的傳遞所有必需的參數來構建一個對象在Create..()工廠方法,通過做工作至極必須委託給工廠,而不是初始化所有人後來自己...

對於用的形式給出作爲工廠方法參數 - 通過接口來抽象Client,所以如果通過注入Creator模擬工具來測試這個工廠是非常容易的,那麼工廠也會非常靈活。

摘要:

  • 保持對象不變
  • 委託對象的創建工作,工廠不工廠飛翔距離之間將它分割,這是不乾淨
  • 摘要所有輸入參數的接口,那麼代碼會不易耦合且易於測試