У меня есть следующий код для реализации "фабричного" шаблона проектирования.
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;
}
Конечно, цветовой класс должен быть адаптирован к используемому вами API рисования - и, возможно, быть более продвинутым, чем этот (другие цветовые пространства и т. д.).
Причина, по которой нет смысла использовать шаблоны, заключается в том, что (предположительно) единственная разница между различными операциями рисования - это переменная цвета. Итак, используя шаблоны (или вручную объявляя разные классы, как вы), вы дублируете аналогичный код. Это увеличит вашу программу и замедлит ее.
Итак, функция рисования должна либо принимать цвет в качестве аргумента, либо (как в моем примере) иметь цвет в качестве члена данных класса.
Это называется конструктором копирования и будет вызываться в неожиданные моменты. Поскольку код написан, в этом нет особого смысла, кроме как сказать, что когда я копирую перо, я копирую цвет пера. Если бы Color был более сложным, это могло бы иметь гораздо большее значение.
В качестве дополнения к моему другому ответу, просто чтобы обсудить шаблон Factory и использование шаблона:
Основная (и самая простая) причина использования шаблонов заключается в том, что ваш код идентичен во всех случаях, за исключением типов данных, с которыми он работает. Примеры здесь - контейнеры STL. Можно было бы написать фабричную функцию createVector ("string") и печатать каждый контейнер вручную, но это явно неоптимально.
Даже когда код отличается не только типами данных, можно использовать специализации шаблонов, но во многих случаях фабричная функция будет иметь больше смысла.
В качестве примера рассмотрим библиотеку абстракции базы данных. Можно было бы использовать специализации шаблонов, чтобы библиотеку можно было использовать как «db :: driver». Но это вынудит вас вводить тип базы данных везде в коде (что в первую очередь делает библиотеку бесполезной ...) или выполнять случай с типом интерфейса db :: driver class.
В этом примере более интуитивно понятно сказать db :: get_driver (odbc) и вернуть правильное приведение класса к типу интерфейса.
Ваша фабрика в порядке. Я полагаю, что BluePen и т. д. Были всего лишь примерами имен классов. Вы можете использовать шаблоны при соблюдении следующего условия:
When you know at compile time (i.e when you write code) that you want a specific type returned, then use a template. Otherwise, you can't.
Это означает, что в коде вы можете сделать это:
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 >());
}
Другой способ - динамически зарегистрировать функцию создатель в динамическом объекте Factory.
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
}
Тогда задача Factory будет заключаться в хранении и поиске этих функций создатель. Остальное оставляю как упражнение;) т.е. использование макроса METADECL
Если вам нужна дополнительная информация, см. здесь в главе 4.1 Мета-информация, которая также включает метод расширения, чтобы включить возможности для функций инспектор
Я узнал об этом, используя ET ++, который был проектом по переносу старого MacApp на C++ и X11. В результате Эрик Гамма и другие начали думать о Шаблоны проектирования.
И ... (7 мая 2011 г.) Наконец-то пришло время отправить пример на github
https://github.com/epatel/cpp-factory
У меня есть отдельный вопрос SO относительно этого ответа: stackoverflow.com/questions/3770654/… Есть шанс, что вы могли бы взглянуть? Заранее спасибо.
Вы можете написать общий класс фабрики объектов как шаблонный класс (или использовать тот, который хорошо описан в этом статья gamedev.net).
Таким образом, если у вас в коде более одной фабрики, вам будет проще определить каждую фабрику.
В чем смысл конструктора копирования по ссылке в вашем коде?