C++ 聪明可以避免MI方法的一些问题,尽管不是全部。除此之外,它还创建了一个奇怪的继承层次结构,并增加了编译时的复杂性。一旦我们向Shape层次结构添加更多类,它也会崩溃,因为Drawable只知道如何绘制Rect 接见者模式
允许我们将遍历对象层次与操作对象层次的算法分离。因为每个对象都知道自己的类型,所以即使不知道算法是什么,它也可以调度适当的算法。使用C++ 聪明可以避免MI方法的一些问题,尽管不是全部。除此之外,它还创建了一个奇怪的继承层次结构,并增加了编译时的复杂性。一旦我们向Shape层次结构添加更多类,它也会崩溃,因为Drawable只知道如何绘制Rect 接见者模式,c++,inheritance,multiple-inheritance,crtp,C++,Inheritance,Multiple Inheritance,Crtp,允许我们将遍历对象层次与操作对象层次的算法分离。因为每个对象都知道自己的类型,所以即使不知道算法是什么,它也可以调度适当的算法。使用Shape、Circle和Rect进行说明: class Shape { public: virtual void accept(class Visitor &v) = 0; }; class Rect : public Shape { public: float width; float heig
Shape
、Circle
和Rect
进行说明:
class Shape
{
public:
virtual void accept(class Visitor &v) = 0;
};
class Rect : public Shape
{
public:
float width;
float height;
void accept(class Visitor &v)
{
v.visit(this);
}
};
class Circle : public Shape
{
public:
float radius;
void accept(class Visitor &v)
{
v.visit(this);
}
};
class Visitor
{
public:
virtual void visit(Rect *e) = 0;
virtual void visit(Circle *e) = 0;
};
class ShapePainter : public Visitor
{
// Provide graphics-related implementations for the two methods.
};
class ShapeSerializer : public Visitor
{
// Provide methods to serialize each shape.
};
通过这样做,我们牺牲了一些运行时复杂性,但将我们的各种关注点与数据解耦。现在在程序中添加新的关注点很容易。我们所需要做的就是添加另一个Visitor
类来完成我们想要的任务,并将Shape::accept()
与这个新类的对象结合使用,如下所示:
class ShapeResizer : public Visitor
{
// Resize Rect.
// Resize Circle.
};
Shape *shapey = new Circle();
ShapeResizer sr;
shapey->accept(sr);
这种设计模式还有一个优点,即如果您忘记实现某些数据/算法组合,但在程序中使用它,编译器会抱怨。我们可能希望稍后重写Shape::accept()
,以定义(比如)聚合形状类型,如ShapeStack
。这样我们可以遍历并绘制整个堆栈
我认为,如果性能在您的项目中不是至关重要的,那么访问者解决方案就更优越。如果您需要满足实时约束条件,但这不会降低程序的速度,从而影响到最后期限的完成,那么也可能值得考虑。然后,您应该发展您的想法:TS可以提供启用的
和禁用的策略实施。缺点是这套政策总是固定不变的。谢谢你给出了一个非常有见地的答案。我会考虑访客模式。但我想知道最终用户的语法是什么?使用Crtp模式,我可以使用Box->addChild(Box*)、Box->serialize()等。如果您有兴趣为最终用户提供一个更简单的界面(例如,如果您的代码是一个库),您可以创建一个外观类似于形状的外观对象(Rect
、Box
、Circle
等)但知道在调用Box->serialize()
时如何在全局序列化程序
对象上使用Visitor。通过这种方式,您不会使MI变得复杂,但仍然可以在库中保持灵活性,同时为用户提供易于使用的界面。希望能有帮助,我明白了。当我不知道我的盒子将启用哪些功能时,我仍然不清楚如何构建这个facade对象?我不能为每一个可能的变体构建一个facade。不,你不能为每一个可能的组合构建一个facade对象,你也不应该。但是,您应该决定是否要向最终用户隐藏复杂性,并提供一个简单的界面,或者让用户能够访问库的全部功能和复杂性。事实上,您可以使用CRTP来获得您想要的语法类型,但是它带来了我已经提到的许多问题。如果你问我,我会设计一个简单的通用案例,同时仍然允许在需要做一些不同的事情时访问更复杂的内部结构。
class Rect{
public:
float width;
float height;
}
template <class RectType>
class Stackable{
public:
void addChild(RectType* c){
children.push_back(c);
}
std::vector<RectType*> children;
}
template <class RectType>
class Drawable{
public:
virtual void draw(){
RectType* r static_cast<RectType>(this);
drawRect(r->width, r->height);
}
}
template <class RectType>
class Styleable{
public:
int r, g, b;
}
class CustomRect: public Rect, public Stackable<CustomRect>, public Drawable<CustomRect>{
}
class CustomRectWithStyle: public Rect, public Stackable<CustomRect>, public Drawable<CustomRect>, public Styleable<CustomRect>{
public:
}
#define FEATURED(FEATURE, VALUE) \
template <template<class>class Feature = FEATURE> \
typename std::enable_if<std::is_base_of<Feature<RectType>, RectType>::value == VALUE>::type
template <class RectType>
class Styleable;
template <class RectType>
class Drawable{
public:
FEATURED(Styleable, true)
drawImpl()
{
std::cout << "styleable impl\n";
}
FEATURED(Styleable, false)
drawImpl()
{
std::cout << "not styleable impl\n";
}
virtual void draw(){
drawImpl();
}
};
class Shape
{
public:
virtual void accept(class Visitor &v) = 0;
};
class Rect : public Shape
{
public:
float width;
float height;
void accept(class Visitor &v)
{
v.visit(this);
}
};
class Circle : public Shape
{
public:
float radius;
void accept(class Visitor &v)
{
v.visit(this);
}
};
class Visitor
{
public:
virtual void visit(Rect *e) = 0;
virtual void visit(Circle *e) = 0;
};
class ShapePainter : public Visitor
{
// Provide graphics-related implementations for the two methods.
};
class ShapeSerializer : public Visitor
{
// Provide methods to serialize each shape.
};
class ShapeResizer : public Visitor
{
// Resize Rect.
// Resize Circle.
};
Shape *shapey = new Circle();
ShapeResizer sr;
shapey->accept(sr);