Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/336.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 工厂模式的使用_C#_.net_Factory_Factory Pattern - Fatal编程技术网

C# 工厂模式的使用

C# 工厂模式的使用,c#,.net,factory,factory-pattern,C#,.net,Factory,Factory Pattern,工厂的哪种使用方式更好(正确) 或者我应该抛弃PacketFactory中的第二种方法并使用这个方法 IPacket info = PacketFactory.CreatePacketObject(PacketType.Info); info.CreationTime = DateTime.Now; info.Creator = currentUser; info.Data = " disconnected"; 或者其他什

工厂的哪种使用方式更好(正确)

或者我应该抛弃PacketFactory中的第二种方法并使用这个方法

IPacket info = PacketFactory.CreatePacketObject(PacketType.Info);
            info.CreationTime = DateTime.Now;
            info.Creator = currentUser;
            info.Data = " disconnected";
或者其他什么

包装厂代码:

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;
        }

    }

依我看,这取决于创建数据包的有效实例是否需要CreationTime、Creator和Data。如果是,我将坚持解决方案一,并要求尽早设置这些属性,以工厂方法为例。如果这些属性不应该在以后的某个时刻更改,我会另外将这些属性设置为只读。
如果设置属性是可选的,请保持factory界面干净,并移除具有属性的重载。

在应用模式之前,您应该清楚地知道这样做会带来什么好处,在本例中,我并不认为引入静态“factory”会给您带来什么好处。从
PacketFactory
的客户机的角度来看:引入它是否减少了客户机与
IPacket
的各种具体实现者之间的耦合?我不这么认为,因为客户端必须通过指定
PacketType.Info
PacketType.Message
PacketType.Log
的枚举值来知道它想要哪种
IPacket
。这与客户了解
信息
消息
日志
类有什么不同?从“工厂”开始是一个静态类。客户端与返回的
IPacket
类型的耦合程度与调用相应的
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方法,那么您可以将它们添加到接口中。其中一种形式是传递具有各种属性/方法的对象,
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
的类型,因此,如果在实现这一方法的同时,你开始感觉到客户端< /C>开始隐式地知道正在构建的特定类型,那么你可能需要退后一步,考虑一下工厂是否合适。您唯一的其他选择是在构建工厂时提供额外信息(即,将其传递给构造函数)


这些代表了一些选项,但在任何情况下,我强烈建议您不要使用静态或单例“工厂”类,因为它会将您的客户机类与工厂以及最有可能的
IPacket
子类强烈耦合。

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

  • 您可以将所有
    IPacket
    属性标记为只读,并且所有实例都是不可变的
  • Create..()
    factory方法中传递构建对象所需的所有参数非常明显,而不是稍后自己通过执行必须委托给工厂的作业来初始化所有参数
将带有
客户端创建者
的aproach视为工厂方法参数-通过接口进行抽象
客户端
,因此,如果通过注入创建者模拟来测试此工厂将非常容易,工厂也将非常灵活

总结:

  • 保持对象不变
  • 将对象创建工作委托给工厂,不要在工厂日历之间拆分,这是不干净的
  • 通过接口抽象所有的输入参数,这样代码就不会那么耦合,也更容易测试

没有上下文很难说。在你的简单示例中,我根本不会使用工厂。如果你在生产代码中铸造工厂的结果(而不仅仅是一个示例),那就是一种代码味道。仅通过接口引用工厂的结果。@TrueWill ok,现在是什么情况?@CodeInChaos原始代码扩展得更多,因此工厂将很有用here@Saint-更好。另外,在开关的默认情况下,我会抛出一个异常。有人可以添加一个PacketType(如果它是一个枚举,也可以将任何数字转换为PacketType),工厂将返回null。这是一个不错的解决方案,但是如果我想使用其他构造函数,例如在MessageFactory中使用2个附加参数。“我应该如何建造这些工厂?”Saint-添加了一个新的部分。回答得好!它帮助我了解了工厂模式
//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);
}
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
}