Вопрос

У меня есть следующий код для реализации шаблона проектирования "factory".

class Pen{
public:
     virtual void Draw() = 0;
};

class RedPen : public Pen{
public:
     virtual void Draw(){
         cout << "Drawing with red pen" << endl;
     }
};

class BluePen : public Pen{
public:
     virtual void Draw(){
         cout << "Drawing with blue pen" << endl;
     }
};

auto_ptr<Pen> createPen(const std::string color){
     if(color == "red")
         return auto_ptr<Pen>(new RedPen);
     else if(color == "blue")
         return auto_ptr<Pen>(new BluePen);
}

Но я слышал, что это можно сделать лучшим способом, используя "Шаблоны C ++".Кто-нибудь может помочь, как это делается и чем шаблонный подход лучше этого?

Есть какие-нибудь мысли

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

Решение

В примере, который вы опубликовали, ни фабричный, ни шаблонный подход не имеют для меня смысла.Мое решение включает элемент данных в классе Pen.

class Pen {
public:
    Pen() : m_color(0,0,0,0) /* the default colour is black */
    {            
    }

    Pen(const Color& c) : m_color(c)
    {
    }

    Pen(const Pen& other) : m_color(other.color())
    {
    }

    virtual void Draw()
    {
        cout << "Drawing with a pen of color " << m_color.hex();
    }
    void setColor(const Color& c) { m_color = c; }
    const Color& color() const { return m_color; }
private:
    Color m_color;
};

class Color {
public:
    Color(int r, int g, int b, int a = 0) :
        m_red(r), m_green(g), m_blue(other.blue()), m_alpha(a)  
    {
    }

    Color(const Color& other) : 
        m_red(other.red()), m_green(other.green()), 
        m_blue(other.blue()), m_alpha(other.alpha())
    {
    }

    int red() const { return m_red; }
    int green() const  { return m_green; }
    int blue() const { return m_blue; }
    int alpha() const { return m_alpha; }

    std::string hex() const
    {
        std::ostringstream os;
        char buf[3];
        os << "#";

        sprintf(buf, "%2X", red());
        os << buf;

        sprintf(buf, "%2X", green());
        os << buf;

        sprintf(buf, "%2X", blue());
        os << buf;

        sprintf(buf, "%2X", alpha());
        os << buf;

        return os.str();
    }

private:
    int m_red;
    int m_green;
    int m_blue;
    int m_alpha;
}

Конечно, класс color должен быть адаптирован к используемому вами API рисования - и, возможно, быть намного более продвинутым, чем этот (другие цветовые пространства и т.д.).

Почему не шаблоны?

Причина, по которой не имеет смысла использовать шаблоны, заключается в том, что (предположительно) единственной разницей между различными операциями рисования является переменная цвета.Таким образом, используя шаблоны (или вручную объявляя разные классы, как вы это делали), вы будете дублировать аналогичный код.Это сделает вашу программу большой и замедлит ее работу.

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

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

Другой способ заключается в динамической регистрации Автор функция для динамического заводского объекта.

BluePen *create_BluePen() { return new BluePen; }
static bool BluePen_creator_registered = 
                       Factory::instance()->registerCreator("BluePen", 
                                                            create_BluePen);

Одним интересным эффектом при подобном выполнении является то, что статическая переменная bool BluePen-creator-registered будет установлен заранее main() запускает, таким образом, автоматизацию регистрации.

Эти строки иногда создаются с помощью обычных макросов, т.е. как

#define METAIMPL( _name ) \
_name *create_ ## _name() { return new _name; } \
static bool _name ## _creator_registered = \
                        Factory::instance()->registerCreator(# _name, \
                                                             create_ ## _name)

...и используется рядом с конструктором

METAIMPL( BluePen ); // auto registers to the Factory

BluePen::BluePen() : Pen() {
   // something
}

Тогда задачей фабрики будет хранение и поиск этих данных Автор функции.Я оставляю все остальное как есть. упражнение ;) т. е. использование макроса METADECL

Если вам нужна дополнительная информация, смотрите здесь в соответствии с главой 4.1 Метаинформация который также включает в себя метод расширения для включения возможностей для инспектор возможности

Я научился этому, используя ET++ это был проект по портированию старого MacApp на C ++ и X11.В попытке добиться этого Эрик Гамма и др. Начали думать о Шаблоны проектирования

И...(7 мая 2011) Наконец-то решился выложить пример на github
https://github.com/epatel/cpp-factory

С вашей фабрикой все в порядке.Я так понимаю, что BluePen и так далее были просто примеры названий классов.Вы можете использовать шаблоны, если выполняется следующее условие:

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

В коде это означает, что вы можете сделать это:

template<typename PenType>
auto_ptr<Pen> createPen(){
    return auto_ptr<Pen>(new PenType);
}

Имея это на месте, вы можете затем использовать это следующим образом

...
auto_ptr<Pen> p = createPen<BluePen>();
...

Но этот шаблон-аргумент, тот BluePen, не может быть переменной, которой присваивается тип во время выполнения.В вашем примере вы передаете строку, которая, конечно, может быть задана во время выполнения.Итак, когда вы читаете, что вы можете использовать шаблоны C ++, то эта рекомендация верна лишь условно - тогда, когда ваше решение, из какого пера создавать, уже принято во время компиляции.Если это условие соответствует, то шаблонным решением будет тот самый правильный поступок.Это ничего не будет стоить вам во время выполнения и будет именно тем, что вам нужно.

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

struct Red{};
struct Blue{};

template < typename Color >
class Pen{};

template <>
class Pen< Red >
{
     void Draw(){
         cout << "Drawing with red pen" << endl;
     }
};

template <>
class Pen< Blue >
{
     void Draw(){
         cout << "Drawing with blue pen" << endl;
     }
};

template < typename Color >
std::auto_ptr< Pen< Color > > createPen()
{
     return auto_ptr< Pen< Color > >(new Pen< Color >());
}

Вы могли бы написать универсальный класс фабрики объектов в качестве шаблонного класса (или использовать тот, который хорошо описан в этом gamedev.net статья).

Таким образом, если у вас в коде более одной фабрики, определение каждой фабрики требует меньше усилий.

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

Основная (и самая простая) причина использования шаблонов заключается в том, что ваш код идентичен во всех случаях, за исключением типов данных, с которыми он работает.Примерами здесь являются контейнеры STL.Можно было бы написать заводскую функцию createVector("строка") и вводить каждый контейнер вручную - но это явно неоптимально.

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

В качестве примера рассмотрим библиотеку абстракции базы данных.Можно было бы использовать специализации шаблонов, чтобы библиотеку можно было использовать как "db:: driver".Но это вынудило бы вас вводить тип базы данных везде в коде (что в первую очередь сделало бы библиотеку бесполезной ...) или выполнить обращение к интерфейсу типа db::driver class.

В этом примере более интуитивно понятно сказать db::get_driver(odbc) и вернуть правильное приведение класса к типу интерфейса.

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