C# 工厂模式的使用
工厂的哪种使用方式更好(正确) 或者我应该抛弃PacketFactory中的第二种方法并使用这个方法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"; 或者其他什
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
的类型,因此,如果在实现这一方法的同时,你开始感觉到这些代表了一些选项,但在任何情况下,我强烈建议您不要使用静态或单例“工厂”类,因为它会将您的客户机类与工厂以及最有可能的
IPacket
子类强烈耦合。我建议第一种方法是这样做的
- 您可以将所有
属性标记为只读,并且所有实例都是不可变的IPacket
- 在
factory方法中传递构建对象所需的所有参数非常明显,而不是稍后自己通过执行必须委托给工厂的作业来初始化所有参数Create..()
客户端创建者
的aproach视为工厂方法参数-通过接口进行抽象客户端
,因此,如果通过注入创建者模拟来测试此工厂将非常容易,工厂也将非常灵活
总结:
- 保持对象不变
- 将对象创建工作委托给工厂,不要在工厂日历之间拆分,这是不干净的
- 通过接口抽象所有的输入参数,这样代码就不会那么耦合,也更容易测试
//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
}