Перед применением паттерна у вас должно быть четкое представление о том, что вы получаете, делая это, и в этом случае я не вижу, что введение статической «Фабрики» вам что-то приносит. Взгляните на это с точки зрения клиента PacketFactory
: снизило ли оно взаимодействие между клиентом и различными конкретными разработчиками IPacket
? Я бы не стал спорить, поскольку клиент уже должен знать, какой тип IPacket
он хочет, указав значение перечисления: PacketType.Info
, PacketType.Message
или PacketType.Log
. Чем это отличается от того, что клиент знает о классах Info
, Message
и Log
? Поскольку «Фабрика» является статическим классом, клиент точно так же связан с типом IPacket
, который возвращается, как если бы он просто вызывал конструктор соответствующего IPacket
разработчика, поскольку вам пришлось бы изменить клиента в для того, чтобы он работал с другим типом IPacket
в любом случае.
Итак, если вам действительно нужно использовать фабрику какого-то типа, я бы предложил использовать шаблон Абстрактной фабрики, чтобы клиенты фабрики зависели только от интерфейса фабрики и, следовательно, могли работать с различными типами *. 1015 * без необходимости изменения. Например:
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
исполнителей требуются дополнительные параметры:
В шаблоне Abstract Factory должен быть единый интерфейс для всех разработчиков, поэтому, если для всех фабрик имеет смысл иметь метод Create с дополнительными параметрами, вы можете добавить их в интерфейс. Одной из форм этого является передача объекта с различными свойствами / методами, которые метод Create
может использовать для получения дополнительных значений параметров, в которых он нуждается. Особый случай - Double Dispatch, когда вызывающая сторона передает себя (в данном случае Клиент) и затем вызывается изнутри метода 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
}
Они представляют некоторые из вариантов, но в любом случае, я бы настоятельно рекомендовал вам не использовать статический или одноэлементный класс "Factory", потому что он будет сильно связывать ваш клиентский класс с фабрикой и, скорее всего, IPacket
подкласс.