Вопрос

Я не знаю, есть ли официальное название для этого, но я играю с тем, что мне нравится называть «самозаводом». шаблон. По сути, это когда абстрактный базовый класс действует как фабрика для себя. Позвольте мне объяснить:

В моей системе есть объекты Foo и Bar, которые используются через интерфейсы FooInterface и BarInterface. Мне нужно дать своим клиентам правильный тип Foo and Bar. Решение о том, какой конкретный объект Foo создать, принимается во время компиляции. Например, если вы компилируете на win32, вы хотите создавать только объекты Win32Foo, а если вы компилируете на OSX, вы хотите создавать только объекты OSXFoo и так далее. Но решение о том, какой конкретный объект Bar создавать, принимается во время выполнения на основе ключевой строки.

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

shared_ptr<FooInterface> foo = FooFactory::create();
shared_ptr<BarInterface> happyBar = BarFactory::create("Happy");
shared_ptr<BarInterface> sadBar = BarFactory::create("Sad");

Другой способ - использовать то, что я называю "самозаводами":

shared_ptr<FooInterface> foo = FooInterface::create();
shared_ptr<BarInterface> happyBar = BarInterface::create("Happy");
shared_ptr<BarInterface> sadBar = BarInterface::create("Sad");

Каковы плюсы и минусы каждого подхода, как с точки зрения удобства использования, так и с точки зрения архитектуры?

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

Решение

Фабрики имеют два общих применения:

1) Определите динамический полиморфный тип во время выполнения на основе параметров и / или глобального состояния (такого как конфигурация). Ваш шаблон делает это.

2) Внедрение зависимости: вместо использования статической функции для создания объектов используйте фабрику объект , чтобы вызывающий объект мог настроить тип возвращаемого объекта, передав любую фабрику, которую они хочу. Ни один из этих шаблонов не обеспечивает это. Более того, ваш второй шаблон даже не позволяет внедрять статические зависимости (имея функции шаблона, которые принимают класс фабрики в качестве параметра шаблона), потому что интерфейс и фабрика одинаковы.

Итак, одним из недостатков вашего паттерна (и ваших обычных фабрик) является то, что внедрение зависимостей действительно не поддерживается. Есть одна и только одна функция для вызова объекта, который является FooInterface, и это FooInterface :: create (). Я не буду спорить, почему внедрение зависимостей полезно, просто отметьте, что если вы строите таким образом, вы не сможете его использовать.

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

Я бы сделал улучшение:

shared_ptr<FooInterface> foo = Factory<FooInterface>::create();
shared_ptr<BarInterface> happyBar = Factory<BarInterface>::create("Happy");
shared_ptr<BarInterface> sadBar = Factory<BarInterface>::create("Sad");

Вы бы заявили:

template <class I>
struct Factory { };

И затем для каждого интерфейса, которому требуется фабрика, вы должны сделать это:

template <>
struct Factory<FooInterface>
{
    static FooInterface create();
};

Это позволяет отделить реализацию фабрики от объявления интерфейса, но при этом использовать систему типов для их привязки во время компиляции.

Обычно фабрики отвечают за создание объектов всей иерархии классов. Таким образом, в вашем примере у вас будут Win32Factory, OSXFactory и т. Д. Одним из преимуществ этого является то, что вам нужно выбрать конкретную реализацию (win32 / unix / etc) только один раз - во время создания фабрики, но если вы используете интерфейсы классов, у вас есть постоянно предоставлять информацию об ОС.

Если у вас есть только два класса (Foo и Bar), я не уверен, стоит ли прилагать усилия для создания для них фабрик, а не просто использовать метод create для интерфейсов.

Да, и когда интерфейс имеет метод для создания объектов своего типа, он называется фабричным методом. шаблон .

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