双分派和工厂模式
Double dispatch and factory pattern
我目前有以下代码(不工作):
#include <iostream>
#include <vector>
class Circle;
class Rectangle;
class Shape {
private:
Shape() {};
public:
virtual ~Shape() {};
friend class Circle;
friend class Rectangle;
};
class Creator {
public:
virtual ~Creator() {};
virtual Shape* create() = 0;
virtual bool equals(Shape& s) { return false; };
};
class Circle : public Shape {
private:
Circle() : Shape() {};
public:
class CircleCreator : public Creator {
public:
virtual Shape* create() { return new Circle(); };
virtual bool equals(Shape& other_shape) { return false; };
};
};
class Rectangle : public Shape {
private:
Rectangle() : Shape() {};
public:
class RectangleCreator : public Creator {
public:
virtual Shape* create() { return new Rectangle(); };
virtual bool equals(Shape& other_shape) { return false; };
};
};
int main() {
/* First step, build the list */
std::vector<Shape*> shapeList;
std::vector<Shape*>::iterator it;
Rectangle::RectangleCreator rc;
Circle::CircleCreator cc;
Shape* s = cc.create();
Shape* s1 = rc.create();
shapeList.push_back(s);
shapeList.push_back(s1);
/* Second step: check if we've got a shape starting from a creator */
for (it = shapeList.begin(); it != shapeList.end(); ++it) {
if (rc.equals(**it)) {
std::cout << "same shape" << std::endl;
}
}
return 0;
}
我的目标是使用工厂模式并避免创建新对象(如果列表中已有该对象)。我尝试使用双分派模式,但在这种情况下应用起来并不容易。我该怎么办?
编辑:由于代码在 "critical" 路径中使用,我想避免 RTTI,如 dynamic_cast 等。
也许像这样的事情可以使用成员变量来完成
#include <iostream>
#include <vector>
enum
{
CIRCLE,
RECTANGLE
};
class Circle;
class Rectangle;
class Shape {
private:
Shape() {};
public:
unsigned shapeType;
virtual ~Shape() {};
friend class Circle;
friend class Rectangle;
};
class Creator {
public:
unsigned shapeType;
virtual ~Creator() {};
virtual Shape* create() = 0;
bool equals(Shape& s) { return (this->shapeType == s.shapeType); };
};
class Circle : public Shape {
private:
Circle() : Shape() {shapeType=CIRCLE;};
public:
class CircleCreator : public Creator {
public:
CircleCreator() {shapeType=CIRCLE;};
virtual Shape* create() { return new Circle(); };
};
};
class Rectangle : public Shape {
private:
Rectangle() : Shape() {shapeType=RECTANGLE;};
public:
class RectangleCreator : public Creator {
public:
RectangleCreator() {shapeType=RECTANGLE;};
virtual Shape* create() { return new Rectangle(); };
};
};
int main() {
/* First step, build the list */
std::vector<Shape*> shapeList;
std::vector<Shape*>::iterator it;
Rectangle::RectangleCreator rc;
Circle::CircleCreator cc;
Shape* s = cc.create();
Shape* s1 = rc.create();
shapeList.push_back(s);
shapeList.push_back(s1);
/* Second step: check if we've got a shape starting from a creator */
for (it = shapeList.begin(); it != shapeList.end(); ++it) {
if (rc.equals(**it)) {
std::cout << "same shape" << std::endl;
}
}
return 0;
}
或者这个 - 使用虚函数 return 类型
#include <iostream>
#include <vector>
enum
{
CIRCLE,
RECTANGLE,
UNKNOWN
};
class Circle;
class Rectangle;
class Shape {
private:
Shape() {};
public:
virtual ~Shape() {};
friend class Circle;
friend class Rectangle;
virtual unsigned iAmA(){return UNKNOWN;};
};
class Creator {
public:
virtual ~Creator() {};
virtual Shape* create() = 0;
virtual bool equals(Shape& s) { return false; };
};
class Circle : public Shape {
private:
Circle() : Shape() {};
virtual unsigned iAmA(){return CIRCLE;};
public:
class CircleCreator : public Creator {
public:
CircleCreator() {};
virtual Shape* create() { return new Circle(); };
virtual bool equals(Shape& other_shape) { return (CIRCLE == other_shape.iAmA()); };
};
};
class Rectangle : public Shape {
private:
Rectangle() : Shape() {};
virtual unsigned iAmA(){return RECTANGLE;};
public:
class RectangleCreator : public Creator {
public:
RectangleCreator() {};
virtual Shape* create() { return new Rectangle(); };
virtual bool equals(Shape& other_shape) { return (RECTANGLE == other_shape.iAmA()); };
};
};
int main() {
/* First step, build the list */
std::vector<Shape*> shapeList;
std::vector<Shape*>::iterator it;
Rectangle::RectangleCreator rc;
Circle::CircleCreator cc;
Shape* s = cc.create();
Shape* s1 = rc.create();
shapeList.push_back(s);
shapeList.push_back(s1);
/* Second step: check if we've got a shape starting from a creator */
for (it = shapeList.begin(); it != shapeList.end(); ++it) {
if (rc.equals(**it)) {
std::cout << "same shape" << std::endl;
}
}
return 0;
}
我不确定你想做什么,但我想这可以为你指明方向
enum class Shapes
{
Rectangle,
Circle,
...
};
class Shape
{
private:
Shapes m_shape;
protected:
Shape(Shapes shape)
{
m_shape = shape;
}
public:
Shapes GetShape() { return m_shape; } // this is used to check whether two shapes are equal
virtual ~Shape() = default;
};
现在对于工厂模式你会做:
class ShapeFactory
{
public:
static Shape* CreateShape(Shapes shape)
{
switch (shape)
{
case Shapes::Circle:
return new Circle();
// etc.
}
}
};
这对我来说感觉很多余而且不是很聪明。此外,这可以将大量代码放在一个地方。
对于调度,你可以这样做(我想,我不是这个概念的真正粉丝,因为它可以通过简单的模板使用变得不那么冗长)
class ShapeCreator
{
public:
virtual Shape* Create() = 0;
virtual ~ShapeCreator() = default;
};
class Circle : public Shape
{
public:
class Creator : ShapeCreator
{
public:
Shape* Create() { return new Circle(); }
};
Circle() : Shape(Shapes::Circle)
{}
};
bool SomethingWithCircle()
{
Circle::Creator circleCreator;
Shape* first = circleCreator.Create();
Shape* second = circleCreator.Create();
// notice memleak here
return first->GetShape() == second->GetShape();
}
如果使用 C++11,你可以走得更远,避免整个想法/这对我来说感觉非常 java- 无论如何/使用适当的模板自慰技术。 (仍然可以应用于 C++11 之前的版本,只是不能指定参数。)
template<class T>
class ShapeCreator
{
public:
template<class... TParams>
static T* Create(TParams&&... parameters) { return new T(std::forward<TParams>(parameters)...); }
};
class Rectangle : public Shape
{
private:
int m_width;
int m_height;
public:
Rectangle(int width, int height) : Shape(Shapes::Rectangle)
{
m_width = width;
m_height = height;
}
};
bool DoSomethingWithRectangles()
{
Rectangle* first = ShapeCreator<Rectangle>::Create(10, 15);
Shape* second = ShapeCreator<Rectangle>::Create(20, 25);
// notice memleak here
return first->GetShape() == second->GetShape();
}
TL;DR
您实际上并不需要 RTTI,但您需要将类型信息存储在基本类型的某处。我为此使用 enum Shapes
。
Factory 和 Dispatch 看起来都是个好主意,但在使用它们时您仍然需要在某处进行动态转换。
您可以使用模板替换这两个模式,但是一旦您获得基础对象的向量,您仍然需要 dynamic_cast
在某些时候。
我没有对此进行任何测量,但我真的对使用虚函数和动态转换的性能比较很感兴趣,因为我想它们会非常相似......
尾注:
请注意,我个人认为在定义基本接口的classes上使用equals
或operator==
这样的方法不是很明智,因为有两种可能的结果:
equals
是虚拟的 -> 慢但可以接受
equals
不是虚拟的 -> 不能在继承类型中使用以实际做更多 advanced/relevant 比较,打破了 Open to extension, closed for modification
的想法
很明显,如果不定义equals
,每次都要写比较代码。或者可能使用一些模板化的 Comparison
class 通过特征进行可能的专业化,再次产生最佳性能而没有代码重复。
一般来说,您可以问问自己 "why isn't there base object and reflection like in java or c# in c++? It would allow me to use all these nice and clever patterns." 答案是模板。为什么要做运行-time,什么时候可以编译时做?
我目前有以下代码(不工作):
#include <iostream>
#include <vector>
class Circle;
class Rectangle;
class Shape {
private:
Shape() {};
public:
virtual ~Shape() {};
friend class Circle;
friend class Rectangle;
};
class Creator {
public:
virtual ~Creator() {};
virtual Shape* create() = 0;
virtual bool equals(Shape& s) { return false; };
};
class Circle : public Shape {
private:
Circle() : Shape() {};
public:
class CircleCreator : public Creator {
public:
virtual Shape* create() { return new Circle(); };
virtual bool equals(Shape& other_shape) { return false; };
};
};
class Rectangle : public Shape {
private:
Rectangle() : Shape() {};
public:
class RectangleCreator : public Creator {
public:
virtual Shape* create() { return new Rectangle(); };
virtual bool equals(Shape& other_shape) { return false; };
};
};
int main() {
/* First step, build the list */
std::vector<Shape*> shapeList;
std::vector<Shape*>::iterator it;
Rectangle::RectangleCreator rc;
Circle::CircleCreator cc;
Shape* s = cc.create();
Shape* s1 = rc.create();
shapeList.push_back(s);
shapeList.push_back(s1);
/* Second step: check if we've got a shape starting from a creator */
for (it = shapeList.begin(); it != shapeList.end(); ++it) {
if (rc.equals(**it)) {
std::cout << "same shape" << std::endl;
}
}
return 0;
}
我的目标是使用工厂模式并避免创建新对象(如果列表中已有该对象)。我尝试使用双分派模式,但在这种情况下应用起来并不容易。我该怎么办?
编辑:由于代码在 "critical" 路径中使用,我想避免 RTTI,如 dynamic_cast 等。
也许像这样的事情可以使用成员变量来完成
#include <iostream>
#include <vector>
enum
{
CIRCLE,
RECTANGLE
};
class Circle;
class Rectangle;
class Shape {
private:
Shape() {};
public:
unsigned shapeType;
virtual ~Shape() {};
friend class Circle;
friend class Rectangle;
};
class Creator {
public:
unsigned shapeType;
virtual ~Creator() {};
virtual Shape* create() = 0;
bool equals(Shape& s) { return (this->shapeType == s.shapeType); };
};
class Circle : public Shape {
private:
Circle() : Shape() {shapeType=CIRCLE;};
public:
class CircleCreator : public Creator {
public:
CircleCreator() {shapeType=CIRCLE;};
virtual Shape* create() { return new Circle(); };
};
};
class Rectangle : public Shape {
private:
Rectangle() : Shape() {shapeType=RECTANGLE;};
public:
class RectangleCreator : public Creator {
public:
RectangleCreator() {shapeType=RECTANGLE;};
virtual Shape* create() { return new Rectangle(); };
};
};
int main() {
/* First step, build the list */
std::vector<Shape*> shapeList;
std::vector<Shape*>::iterator it;
Rectangle::RectangleCreator rc;
Circle::CircleCreator cc;
Shape* s = cc.create();
Shape* s1 = rc.create();
shapeList.push_back(s);
shapeList.push_back(s1);
/* Second step: check if we've got a shape starting from a creator */
for (it = shapeList.begin(); it != shapeList.end(); ++it) {
if (rc.equals(**it)) {
std::cout << "same shape" << std::endl;
}
}
return 0;
}
或者这个 - 使用虚函数 return 类型
#include <iostream>
#include <vector>
enum
{
CIRCLE,
RECTANGLE,
UNKNOWN
};
class Circle;
class Rectangle;
class Shape {
private:
Shape() {};
public:
virtual ~Shape() {};
friend class Circle;
friend class Rectangle;
virtual unsigned iAmA(){return UNKNOWN;};
};
class Creator {
public:
virtual ~Creator() {};
virtual Shape* create() = 0;
virtual bool equals(Shape& s) { return false; };
};
class Circle : public Shape {
private:
Circle() : Shape() {};
virtual unsigned iAmA(){return CIRCLE;};
public:
class CircleCreator : public Creator {
public:
CircleCreator() {};
virtual Shape* create() { return new Circle(); };
virtual bool equals(Shape& other_shape) { return (CIRCLE == other_shape.iAmA()); };
};
};
class Rectangle : public Shape {
private:
Rectangle() : Shape() {};
virtual unsigned iAmA(){return RECTANGLE;};
public:
class RectangleCreator : public Creator {
public:
RectangleCreator() {};
virtual Shape* create() { return new Rectangle(); };
virtual bool equals(Shape& other_shape) { return (RECTANGLE == other_shape.iAmA()); };
};
};
int main() {
/* First step, build the list */
std::vector<Shape*> shapeList;
std::vector<Shape*>::iterator it;
Rectangle::RectangleCreator rc;
Circle::CircleCreator cc;
Shape* s = cc.create();
Shape* s1 = rc.create();
shapeList.push_back(s);
shapeList.push_back(s1);
/* Second step: check if we've got a shape starting from a creator */
for (it = shapeList.begin(); it != shapeList.end(); ++it) {
if (rc.equals(**it)) {
std::cout << "same shape" << std::endl;
}
}
return 0;
}
我不确定你想做什么,但我想这可以为你指明方向
enum class Shapes
{
Rectangle,
Circle,
...
};
class Shape
{
private:
Shapes m_shape;
protected:
Shape(Shapes shape)
{
m_shape = shape;
}
public:
Shapes GetShape() { return m_shape; } // this is used to check whether two shapes are equal
virtual ~Shape() = default;
};
现在对于工厂模式你会做:
class ShapeFactory
{
public:
static Shape* CreateShape(Shapes shape)
{
switch (shape)
{
case Shapes::Circle:
return new Circle();
// etc.
}
}
};
这对我来说感觉很多余而且不是很聪明。此外,这可以将大量代码放在一个地方。
对于调度,你可以这样做(我想,我不是这个概念的真正粉丝,因为它可以通过简单的模板使用变得不那么冗长)
class ShapeCreator
{
public:
virtual Shape* Create() = 0;
virtual ~ShapeCreator() = default;
};
class Circle : public Shape
{
public:
class Creator : ShapeCreator
{
public:
Shape* Create() { return new Circle(); }
};
Circle() : Shape(Shapes::Circle)
{}
};
bool SomethingWithCircle()
{
Circle::Creator circleCreator;
Shape* first = circleCreator.Create();
Shape* second = circleCreator.Create();
// notice memleak here
return first->GetShape() == second->GetShape();
}
如果使用 C++11,你可以走得更远,避免整个想法/这对我来说感觉非常 java- 无论如何/使用适当的模板自慰技术。 (仍然可以应用于 C++11 之前的版本,只是不能指定参数。)
template<class T>
class ShapeCreator
{
public:
template<class... TParams>
static T* Create(TParams&&... parameters) { return new T(std::forward<TParams>(parameters)...); }
};
class Rectangle : public Shape
{
private:
int m_width;
int m_height;
public:
Rectangle(int width, int height) : Shape(Shapes::Rectangle)
{
m_width = width;
m_height = height;
}
};
bool DoSomethingWithRectangles()
{
Rectangle* first = ShapeCreator<Rectangle>::Create(10, 15);
Shape* second = ShapeCreator<Rectangle>::Create(20, 25);
// notice memleak here
return first->GetShape() == second->GetShape();
}
TL;DR
您实际上并不需要 RTTI,但您需要将类型信息存储在基本类型的某处。我为此使用 enum Shapes
。
Factory 和 Dispatch 看起来都是个好主意,但在使用它们时您仍然需要在某处进行动态转换。
您可以使用模板替换这两个模式,但是一旦您获得基础对象的向量,您仍然需要 dynamic_cast
在某些时候。
我没有对此进行任何测量,但我真的对使用虚函数和动态转换的性能比较很感兴趣,因为我想它们会非常相似......
尾注:
请注意,我个人认为在定义基本接口的classes上使用equals
或operator==
这样的方法不是很明智,因为有两种可能的结果:
equals
是虚拟的 -> 慢但可以接受equals
不是虚拟的 -> 不能在继承类型中使用以实际做更多 advanced/relevant 比较,打破了Open to extension, closed for modification
的想法
很明显,如果不定义equals
,每次都要写比较代码。或者可能使用一些模板化的 Comparison
class 通过特征进行可能的专业化,再次产生最佳性能而没有代码重复。
一般来说,您可以问问自己 "why isn't there base object and reflection like in java or c# in c++? It would allow me to use all these nice and clever patterns." 答案是模板。为什么要做运行-time,什么时候可以编译时做?