Using the factory template

What format is better to use Factory?

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

or should I throw the second method into PacketFactory and use it?

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

or maybe some others?

PacketFactory Code:

 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; } } 
+2
source share
3 answers

Before you apply the template, you should have a clear idea of ​​what you get by doing this, in which case I really don’t see that introducing a static “Factory” is gaining you. Look at this from the PacketFactory client PacketFactory : did he introduce it by reducing the connection between the client and various specific IPacket performers? I would not argue that since the client should already know what type of IPacket it wants, specifying the enumeration value is either PacketType.Info , PacketType.Message , or PacketType.Log . How does this differ from a client who knows about the Info , Message and Log classes? Since "Factory" is a static class, the client is also associated with the return type of IPacket , as if it would just call the constructor of the corresponding IPacket constructor, because you would have to change the client to work with another type of IPacket anyway.

So, if you really have to use any Factory, I would suggest using the Abstract Factory template, so that Factory clients depend only on the Factory interface and therefore able to work with different types of IPacket without the need for change. For instance:

 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 decoupled) Client client2 = new Client(new LogFactory()); 

As far as Factory should allow building IPacket without specifying the creator, the data and time of creation are either independent of class invariants. If class invariants can be satisfied when fields are not specified, then this is fine, otherwise they should be necessary. Part of the task of the class should be to make sure that it cannot be constructed in an invalid state, since users of this class will depend on what happens.

In the case when one of the IPacket developers needs additional parameters:

The Factory sample needs to be a single interface for all artists, so if it makes sense to use the Create method with additional parameters for all factories, you can add them to the interface. One of the forms is to transfer an object with various properties / methods that the Create method can use to obtain additional parameter values ​​that it needs. A special case is Double Dispatch, where the caller himself transmits (in this case, the Client) and then is called from the Create method.

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

You must remember that the goal is to abstract the type of IPacket , so if you start to feel that Client beginning to implicitly know which type is being built, then you may need to take a step back and consider whether Factory. The only other option is to provide additional information when building the Factory (i.e., Pass it to the constructor).

 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 } 

Those represent some of the parameters, but in any case, I would strongly recommend that you not use the static or single class "Factory", because it will strongly combine your client class with Factory and most likely an IPacket subclass.

+7
source

IMO depends on whether CreationTime, Creator, and Data are needed to create a valid package instance. If they exist, I would stick with one solution and require that these properties be set at the earliest possible moment, in your case, in your factory method. If these properties are not changed at a later point in time, I would additionally make these properties read-only. If parameter setting is optional, save your factory interface and remove the overload with its properties.

+3
source

I would suggest the first approach this way

  • You can mark all IPacket properties as readonly, and all instances will be immutable
  • It is very clear to pass all the necessary parameters for creating the object in the Create..() factory method, and not to initialize them yourself later by completing the task that should be delegated to the factory ...

Regarding aproach, with the parameter of the Client creator method as factory, it is an abstract Client interface, so if it would be very easy to test this factory, introducing the Creator layout and the factory will be a lot flexible as well.

Summary:

  • Keep objects immutable
  • Delegating the work of creating an object with a factory and not sharing it between factory calee is not clean.
  • Designate all input parameters by interfaces, so the code will be less connected and easily tested
+1
source

Source: https://habr.com/ru/post/1379113/


All Articles