訪問者パターン
3 番目に考えられるアプローチは、抽象仮想 Draw メソッドを基本 Item クラスに配置することであることはわかっていますが、私の状況では、これらのクラスにはデータのみを含めることができ、ロジックを含めることができないため、それを行うことはできません。
ビジター パターンを試してください。すべてのロジックは、オブジェクトではなくビジター クラスになります。各オブジェクトには、accept(Visitor&) という仮想メソッドが 1 つだけあります。
ライブデモ
#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