我有“工厂”设计模式实现的以下代码。
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 ++模板”以更好的方式完成。任何人都可以帮助完成它以及模板方法如何比这更好吗?
任何想法
答案 0 :(得分:12)
另一种方法是将 creator 函数动态注册到动态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的任务是存储和查找这些 creator 函数。我把剩下的作为练习;)即使用METADECL宏
如果您想了解更多信息,请参阅 4.1元信息章节下的here,其中还包括一种扩展方法,包括检查员功能的可能性
我通过使用ET++来学习这一点,这是一个将旧MacApp移植到C ++和X11的项目。为此,Eric Gamma等开始考虑设计模式
并且......(2011年5月7日)最后来到github举一个例子https://github.com/epatel/cpp-factory
答案 1 :(得分:5)
你的工厂很好。我认为BluePen
等等只是示例类名。如果满足以下条件,您可以使用模板:
当您在编译时(即编写代码时)知道要返回特定类型时,请使用模板。否则,你不能。
这意味着在代码中,你可以这样做:
template<typename PenType>
auto_ptr<Pen> createPen(){
return auto_ptr<Pen>(new PenType);
}
完成后,您可以使用
...
auto_ptr<Pen> p = createPen<BluePen>();
...
但是,模板参数BluePen
不能是在运行时设置为类型的变量。在您的示例中,您传递一个字符串,当然可以在运行时设置。因此,当您读到可以使用C ++模板时,那么该建议只是条件正确 - 那么,当您决定创建哪支笔时,已经在编译时完成了。如果该条件适合,则模板解决方案 正确的事情。它不会在运行时花费任何成本,而且正是您所需要的。
答案 2 :(得分:4)
在您发布的示例中,工厂或模板方法对我来说都不合适。 我的解决方案涉及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进行调整 - 也许比这个更先进(不同的颜色空间等)。
使用模板没有意义的原因是(推测)不同绘图操作之间的唯一区别是颜色变量。因此,通过使用模板(或手动声明不同的类,就像您所做的那样),您将复制类似的代码。这将使你的程序变大,并减慢速度。
因此,绘制函数应该将颜色作为参数,或者(如我的例子中)将颜色作为类数据成员。
答案 3 :(得分:3)
通过声明颜色的特殊空类,您可以使用模板完成所有操作。这要求在编译时提供每种颜色选择。通过这样做,您可以避免使用带有虚方法的基类。
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 >());
}
答案 4 :(得分:1)
您可以将通用对象工厂类编写为模板类(或使用此gamedev.net article中很好描述的那个)。
这样,如果代码中有多个工厂,则定义每个工厂的工作量就会减少。
答案 5 :(得分:0)
作为我的其他答案的补充,只是讨论工厂模式与模板使用:
使用模板的主要(也是最简单)原因是您的代码在每种情况下都是相同的,除了它所处理的数据类型。这里的例子是STL容器。 可以编写工厂函数createVector(“string”),并手动输出每个容器 - 但这显然是次优的。
即使代码不同,不仅是数据类型,也可以使用模板特化 - 但在很多情况下,工厂函数会更有意义。
作为示例,请考虑数据库抽象库。可以使用模板特化,以便可以像“db :: driver”一样使用库。但是这会强制你在代码中的任何地方输入数据库类型(首先使库变得无用......),或者对接口类型db :: driver class执行大小写。
在这个例子中,更直观地说db :: get_driver(odbc)并返回适当的类转换为接口类型。