访客模式
我知道第三种可能的方法是在基础 Item 类中放置一个抽象的虚拟 Draw 方法,但我不能这样做,因为在我的情况下,这些类只能包含数据,而没有逻辑。
尝试访问者模式——你所有的逻辑都在访问者类而不是对象中。每个对象只有一个虚拟方法——accept(Visitor&)。
现场演示
#include <iostream>
#include <ostream>
#include <utility>
#include <memory>
#include <vector>
#include <string>
using namespace std;
using Color = int;
struct Text;
struct Image;
struct Rect;
struct Visitor
{
virtual void operator()(const Text &x) const=0;
virtual void operator()(const Image &x) const=0;
virtual void operator()(const Rect &x) const=0;
};
struct IVisitable
{
virtual void accept(Visitor&)=0;
virtual ~IVisitable(){}
};
template<typename Derived>
struct Visitable : IVisitable
{
void accept(Visitor &v) override
{
v(*static_cast<Derived*>(this));
}
};
struct Text: Visitable<Text>
{
string Text = "text";
};
struct Image: Visitable<Image>
{
string FilePath = "path";
};
struct Rect: Visitable<Rect>
{
Color FillColor = 11;
};
struct Draw: Visitor
{
void operator()(const Text &x) const override
{
cout << "Text: " << x.Text << endl;
}
void operator()(const Image &x) const override
{
cout << "image: " << x.FilePath << endl;
}
void operator()(const Rect &x) const override
{
cout << "Rect: " << x.FillColor << endl;
}
};
int main()
{
vector<unique_ptr<IVisitable>> items;
items.emplace_back(new Text);
items.emplace_back(new Image);
items.emplace_back(new Rect);
Draw v;
for(auto &&x: items)
x->accept(v);
}
输出是:
Text: text
image: path
Rect: 11
Boost.Variant
但是,缺点是 Item 类的内存使用不是最佳的,因为每种类型的项目实例都将包含许多它并不真正需要的字段。此外,如果稍后添加了其他项目类型,则 Item 类将与所有新字段混在一起。
将 Boost.Variant视为该技术的优化。您的变体将具有max item的大小,而不是所有字段的累积:
std::vector<boost::variant<Text, Image, Rect>> items;
现场演示
#include <boost/range/algorithm.hpp>
#include <boost/variant.hpp>
#include <iostream>
#include <ostream>
#include <utility>
#include <vector>
#include <string>
using namespace boost;
using namespace std;
using Color = int;
struct Text
{
string Text;
};
struct Image
{
string FilePath;
};
struct Rect
{
Color FillColor;
};
struct Draw : static_visitor<void>
{
void operator()(const Text &x) const
{
cout << "Text: " << x.Text << endl;
}
void operator()(const Image &x) const
{
cout << "image: " << x.FilePath << endl;
}
void operator()(const Rect &x) const
{
cout << "Rect: " << x.FillColor << endl;
}
};
int main()
{
vector<variant<Text, Image, Rect>> items =
{
Text{"text"},
Image{"path"},
Rect{55}
};
Draw v;
for_each(items,apply_visitor(v));
}
输出是:
Text: text
image: path
Rect: 55