Можно ли передавать параметры в метод Factory? - PullRequest
8 голосов
/ 05 марта 2009

Один из способов правильно внедрить 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 ().

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

Ответы [ 7 ]

10 голосов
/ 05 марта 2009

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

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

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

5 голосов
/ 05 марта 2009

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

5 голосов
/ 05 марта 2009

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

4 голосов
/ 05 марта 2009

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

1 голос
/ 05 марта 2009

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

typedef Loki :: SingletonHolder >>> ComponentFactory;

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

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

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

1 голос
/ 05 марта 2009

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

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

1 голос
/ 05 марта 2009

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

...