c# - 工厂模式的使用

标签 c# .net factory factory-pattern

哪种方式使用 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;
        }

    }

最佳答案

在应用模式之前,您应该清楚地了解这样做的好处,在这种情况下,我真的不认为引入静态“工厂”会给您带来任何好处。从 PacketFactory 的客户端的角度来看:引入它是否降低了客户端与 IPacket 的各种具体实现者之间的耦合?我认为不会,因为客户端已经必须通过指定 IPacketPacketType.InfoPacketType.Message 的枚举值来知道它想要哪种 PacketType.Log 。这与了解 InfoMessageLog 类的客户端有何不同?由于“工厂”是一个静态类,因此客户端与返回的 IPacket 类型耦合,就像它只是调用适当的 IPacket 实现者的构造函数一样,因为您必须更改客户端才能使其在任何一种情况下都使用不同类型的 IPacket

所以,如果你真的必须使用某种工厂,那么我建议使用抽象工厂模式,这样工厂的客户将只依赖于工厂接口(interface),因此能够使用不同类型的 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 实现者之一需要额外参数的情况下:

抽象工厂模式需要为所有实现者提供一个统一的接口(interface),因此如果所有工厂都有一个带有额外参数的 Create 方法是有意义的,那么您可以将它们添加到接口(interface)中。一种形式是传递一个具有各种属性/方法的对象,Create 方法可以使用这些属性/方法来派生它需要的额外参数值。一种特殊情况是 Double Dispatch,其中调用者传递自身(在本例中为 Client),然后从 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 子类.

关于c# - 工厂模式的使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7945541/

相关文章:

c# - ASP.NET Web Api OData v4 - 如何通过 $metadata 反射(reflect)小数位数和精度?

.net - OWIN OAuth 2.0 - 不记名 token 永不过期

java - 如何动态注入(inject)spring bean(原型(prototype)范围)

c# - .NET CLR 是否预先计算属性值?

c# - 这段代码中的设计模式是什么?

domain-driven-design - 领域模型工厂可以调用存储库吗?

c# - Elasticsearch.net 客户端无法进行基本搜索

c# - 如何使用 ServiceStack DTO TranslateTo 和 PopulateWith?

c# - 通用类型父列表不接受子类型是父列表类型的子类型

.net - VS2015尝试运行应用程序时出错: Invalid Pointer