Использование заводского образца

2

В каком формате лучше использовать 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;
        }

    }
  • 0
    Без контекста это сложно сказать. В вашем простом примере я бы не использовал фабрику вообще.
  • 0
    Если вы приводите результат фабрики в производственный код (а не просто в качестве примера), это запах кода. Ссылаться на результат фабрики исключительно через интерфейс.
Показать ещё 3 комментария
Теги:
factory
factory-pattern

3 ответа

7
Лучший ответ

Перед тем, как применить шаблон, вы должны иметь четкое представление о том, что вы получаете, делая это, и в этом случае я действительно не вижу, что введение статического "Factory" набирает вас. Посмотрите на это с точки зрения клиента PacketFactory: он представил его, уменьшив связь между клиентом и различными конкретными исполнителями IPacket? Я бы не утверждал, что, поскольку клиент уже должен знать, какой тип IPacket он хочет, указав значение перечисления либо PacketType.Info, PacketType.Message, либо PacketType.Log. Как это отличается от клиента, знающего о классах Info, Message и Log? Поскольку "Factory" является статическим классом, клиент так же связан с возвращаемым типом IPacket, как и если бы он просто вызвал конструктор соответствующего конструктора IPacket, потому что вам пришлось бы изменить клиент для того, чтобы он работал с другим типом IPacket в любом случае.

Итак, если вы действительно должны использовать какой-либо Factory, я бы предложил использовать шаблон Abstract Factory, чтобы клиенты Factory зависели только от интерфейса Factory и поэтому способный работать с различными типами 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 decoupled)

Client client2 = new Client(new LogFactory());

Насколько Factory должен позволить построить IPacket без указания создателя, данные и время создания или не зависят от инвариантов класса. Если инварианты класса могут выполняться, когда поля не указаны, то это прекрасно, в противном случае они должны быть необходимы. Часть задания класса должна состоять в том, чтобы убедиться, что он не может быть сконструирован в недопустимом состоянии, поскольку пользователи этого класса будут в зависимости от того, что будет.

В случае, когда одному из разработчиков IPacket нужны дополнительные параметры:

Образец 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 начинает неявно знать, какой конкретный тип строится, тогда вам может потребоваться сделать шаг назад и рассмотреть, подходит ли Factory. Единственный другой вариант - предоставить дополнительную информацию при построении Factory (т.е. Передать его конструктору).

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" , потому что он будет сильно сочетать ваш клиентский класс с Factory и большинством вероятно, подкласс IPacket.

  • 0
    Хорошее решение, но если бы я хотел использовать другой конструктор, например, в MessageFactory с 2 дополнительными параметрами. Как я должен построить эти заводы?
  • 0
    @Saint - добавлен новый раздел. НТН.
Показать ещё 1 комментарий
3

IMO зависит от того, необходимы ли CreationTime, Creator и Data для создания действительного экземпляра пакета. Если они есть, я бы придерживался решения one и требовал, чтобы эти свойства были установлены в самый ранний возможный момент, в вашем случае в вашем методе factory. Если эти свойства не будут изменены в более поздний момент времени, я бы дополнительно сделал эти свойства только для чтения. Если настройка параметров не является обязательной, сохраните свой интерфейс factory и удалите перегрузку с ее свойствами.

1

Я бы предложил первый подход таким образом

  • Вы можете пометить все свойства IPacket как readonly, и все экземпляры будут неизменными
  • Очень ясно передать все необходимые параметры для создания объекта в методе Create..() factory, а не самостоятельно инициализировать их позже, выполнив задание, которое должно быть делегировано factory...

В отношении aproach с параметром метода Client creator как factory - сделать абстрактным Client интерфейсом, поэтому, если было бы очень легко протестировать этот factory, введя макет Creator и factory будет много также гибкий.

Резюме:

  • Сохранять объекты неизменяемыми
  • Делегировать работу создания объекта с factory и не разделить его между factory calee, это не чисто.
  • Обозначьте все входные параметры по интерфейсам, поэтому код будет менее связан и легко протестирован

Ещё вопросы

Сообщество Overcoder
Наверх
Меню