Вопрос

Один из способов правильно внедрить Dependency Injection - это отделить создание объекта от бизнес-логики. Как правило, это включает использование Фабрики для создания объекта.

До этого момента я никогда серьезно не задумывался об использовании Фабрики, поэтому прошу прощения, если этот вопрос кажется немного упрощенным:

Во всех примерах Factory Pattern, с которыми я столкнулся, я всегда вижу очень простые примеры, которые не имеют параметризации. Например, вот Фабрика, украденная у отличного Миско Хевери Как думать о " новом " Оператор статья.

class ApplicationBuilder {
  House build() {
    return new House(new Kitchen(
               new Sink(),
               new Dishwasher(),
               new Refrigerator())
           );
  }
}

Что произойдет, если я хочу, чтобы у каждого дома, который я строю, было имя? Я все еще использую шаблон Factory, если переписываю этот код следующим образом?

class ApplicationBuilder {
  House build( const std::string & house_name) {
    return new House( house_name,
                      new Kitchen(new Sink(),
                                  new Dishwasher(),
                                  new Refrigerator())
                    );
  }
}

Обратите внимание, что мой вызов метода Factory изменился с этого:

ApplicationBuilder builder;
House * my_house = builder.build();

На это:

ApplicationBuilder builder;
House * my_house = builder.build("Michaels-Treehouse");

Кстати: я думаю, что концепция отделения экземпляров объекта от бизнес-логики великолепна, я просто пытаюсь понять, как я могу применить ее в своей собственной ситуации. Меня смущает то, что все примеры, которые я видел в шаблоне Factory, никогда не передают никаких параметров в функцию build ().

Чтобы быть ясным: я не знаю название дома до момента, когда мне нужно создать его экземпляр.

Это было полезно?

Решение

Я видел довольно много примеров, в которых используется фиксированный набор аргументов, как в примере с вашим именем, и я использовал их сам, и я не вижу в этом ничего плохого.

Однако есть веская причина, по которой многие учебные пособия или небольшие статьи избегают показывать фабрики, которые перенаправляют параметры в построенные объекты: практически невозможно передать произвольное количество аргументов (даже для нормального ограничения, такого как 6 аргументов). Каждый передаваемый параметр должен быть принят как const T& и T&, если вы хотите сделать его общим.

Однако для более сложных примеров вам необходим экспоненциально растущий набор перегрузок (для каждого параметра, const и неконстантной версии) и идеальная пересылка вообще невозможна (поэтому временные потоки пересылаются как временные, например). Для следующего стандарта C ++ эта проблема решена:

class ApplicationBuilder {
  template<typename... T>
  House *build( T&&... t ) {
    return new House( std::forward<T>(t)...,
                      new Kitchen(new Sink(),
                                  new Dishwasher(),
                                  new Refrigerator())
                    );
  }
};

Таким образом, вы можете позвонить

builder.build("Hello", 13);

И он вернется

new House("Hello", 13, new Kitchen(new Sink(...

Прочитайте статью, на которую я ссылался выше.

Другие советы

Я не понимаю, почему было бы неправильно добавлять этот параметр на вашу фабрику. Но имейте в виду, что вам не следует добавлять много параметров, которые могут оказаться бесполезными для всех объектов, созданных фабрикой. Если вы это сделаете, вы потеряете довольно много преимуществ фабрики!

Мало того, что это приемлемо, но и обычно передавать параметры фабричному методу. Ознакомьтесь с некоторыми примерами . Обычно параметр является типом, указывающим фабрике, что делать, но нет причины, по которой вы не можете добавить другую информацию, необходимую для создания объекта. Я думаю, что вы делаете хорошо.

Идея фабрики состоит в том, что она дает вам экземпляр класса / интерфейса, поэтому нет ничего плохого в передаче параметров. Если бы они были, было бы плохо передавать параметры и в new ().

Я согласен с Бенуа. Подумайте о фабрике для создания чего-то вроде соединений sql, хотя в таком случае необходимо будет передать информацию о соединении на фабрику. Фабрика будет использовать эту информацию для использования правильного протокола сервера и т. Д.

Конечно, почему бы и нет ..!?

Приятной особенностью передачи параметров является то, что она позволяет скрыть реализацию конкретного объекта. Например, в опубликованном вами коде вы передаете параметры конструктору. Однако вы можете изменить реализацию, чтобы они передавались с помощью метода Initiailze . Передавая параметры фабричному методу, вы скрываете природу создания и инициализации объекта от вызывающей стороны.

Взгляните на Loki :: Factory, однако есть реализация, очень похожая на Boost. Пример кода, который я регулярно использую в разных вариантах:

typedef Loki :: SingletonHolder < Loki :: Factory л &; Компонент, std :: string, Loki :: Typelist & Lt; const DataCollection & amp ;, Loki :: Typelist < Game *, Loki :: NullType & Gt; GT &; GT &; GT &; ComponentFactory;

На первый взгляд это может показаться немного странным, однако позвольте мне объяснить этого зверя и насколько он действительно силен. По сути, мы создаем синглтон, который содержит фабрику, большинство параметров - для синглтона, Компонент - наш продукт, std :: string - наш тип идентификатора создания, после этого следует список типов параметров, который требуется для создания Компонентов. (это можно определить с помощью макроса также для менее подробного синтаксиса). После этой строки можно просто сделать:

ComponentFactory :: Instance (). CreateObject (" someStringAssociatedWithConcreteType " ;, anDataCollection, aGamePointer);

Для создания объектов, чтобы зарегистрировать один, просто используйте ComponentFactory :: Instance (). Register () ;. В книге Modern C ++ Design есть отличная глава о деталях.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top