使用工厂模式

时间:2011-10-30 14:44:18

标签: 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;
        }

    }

3 个答案:

答案 0 :(得分:8)

在应用模式之前,你应该清楚地知道你通过这样做获得了什么,在这种情况下,我并没有真正看到引入静态“工厂”正在获得任何东西。从PacketFactory的客户端的角度来看它:引入它减少了客户端与IPacket的各种具体实现者之间的耦合?我不认为,因为客户已经必须通过指定IPacketPacketType.InfoPacketType.Message的枚举值来了解它所需要的PacketType.Log。与客户了解InfoMessageLog类有什么不同?由于“Factory”是一个静态类,因此客户端与正在返回的IPacket类型相同,如果它只调用相应IPacket实现者​​的构造函数,因为它必须更改客户端,以便在任何一种情况下都使用不同类型的IPacket

所以,如果你真的必须使用某种类型的工厂,那么我建议使用抽象工厂模式,以便工厂的客户只依赖于工厂界面,因此能够使用不同种类的{ {1}}无需更改。例如:

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实现者​​需要额外的参数:

抽象工厂模式需要为所有实现者提供统一的接口,因此如果所有工厂都有一个带有额外参数的Create方法,那么您可以将它们添加到接口。其中一种形式是传递具有各种属性/方法的对象,IPacket方法可以使用它来获得所需的额外参数值。一个特殊情况是Double Dispatch,其中调用者自己传递(在本例中为Client),然后从Create方法内部调用。

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

那些代表了一些选项,但无论如何,我强烈建议您不要使用静态或单例“Factory”类,因为它会将您的客户端类强烈地耦合到工厂,很可能是{{ 1}}子类。

答案 1 :(得分:3)

IMO取决于是否需要CreationTime,Creator和Data来创建数据包的有效实例。如果是这样的话,我会坚持使用解决方案之一并要求尽可能早地设置这些属性,在您的工厂方法中。如果不应该在以后的某个时刻更改这些属性,我还会另外制作这些属性。 如果设置属性是可选的,请保持工厂界面清洁并删除具有属性的重载。

答案 2 :(得分:1)

我建议第一种方法是这样的

  • 您可以将所有IPacket属性标记为只读,所有实例都将是不可变的
  • 很明显传递所有必需的参数来在Create..()工厂方法中构建一个对象,而不是通过做一个必须委托给工厂的工作来自己初始化所有这些参数......

关于使用Client creator作为工厂方法参数的方法 - 通过接口进行抽象Client,这样如果通过注入Creator mock来测试这个工厂会很容易,并且工厂将非常灵活好。

要点:

  • 保持对象不可变
  • 将对象创建工作委托给工厂,不要在工厂calee之间拆分,这不是很干净
  • 通过接口抽象所有输入参数,因此代码将更少耦合并且易于测试