在我正在使用的产品中,一个非常基本的场景是类的序列化。通常,要序列化的类会调用其子组件
上的序列化e.g。如果有一个班级s.t.类 A {B; C; D;}然后A.Pack将调用pack 功能B,C,D。
由于存在许多这样的类,因此必须一遍又一遍地复制相同的代码模式。 是否可以将此行为封装在模式中(可能使用模板和继承)
答案 0 :(得分:2)
有助于实现此目的的一种可能设计是使用Composite pattern。你的组件(从维基百科图纸中借用)是 Packable ,它可以实现一个Template Method Pack(),可以这样做:
GetChildren();
for each child:
child.Pack()
PackImpl();
PackImpl()是 Packable 中的纯虚方法,所有继承的类都适当地实现它。 GetChildren()将返回一个STL容器(可能为空),以进行迭代。它可以在 Packable 中实现,以及用于存储子对象的私有成员集合。基本上,您继承了 Packable 中的所有类,实现了PackImpl(),然后就完成了。
请注意,如果您的继承层次结构直接依赖于作为成员的子片段,则会导致问题。如果您在聚合方面已经解决了问题,那么这应该可以正常运行。
答案 1 :(得分:2)
制作模板的常用方法是使用类型列表:
#include <iostream>
// typelist definition
struct Empty {};
template < typename H, typename T = Empty >
struct Cons {
typedef H head;
typedef T tail;
};
// interfaces all items support
class IPack
{
public:
virtual void Pack() = 0;
};
// some packable items
class Fee : public IPack
{
public:
virtual void Pack() {
std::cout << "Packed Fee\n";
}
};
class Fi : public IPack
{
public:
virtual void Pack() {
std::cout << "Packed Fi\n";
}
};
class Fo : public IPack
{
public:
virtual void Pack() {
std::cout << "Packed Fo\n";
}
};
class Fum : public IPack
{
public:
virtual void Pack() {
std::cout << "Packed Fum\n";
}
};
// these two templates create a composite IPack from a list
// of the types of its parts
template <typename Types>
class PackList : public PackList<typename Types::tail>
{
protected:
typedef typename Types::head Item;
Item item;
public:
virtual void Pack() {
item.Pack();
PackList<typename Types::tail>::Pack();
}
};
template <>
class PackList<Empty> : public IPack
{
public:
virtual void Pack() {}
};
// FeeFiFoFum is a composite of four items
class FeeFiFoFum : public PackList<Cons<Fee,Cons<Fi,Cons<Fo,Cons<Fum> > > > >
{
};
// create a FeeFiFoFum and call pack on it, which calls pack on its parts
int main ()
{
FeeFiFoFum giant;
giant.Pack();
}
从类型列表创建的合成项的正确实现为您提供了成员的访问者等等,但这足以显示它们的工作方式,并打印出包含Fee,Fi,Fo和Fum的内容而不指定任何行为。
答案 2 :(得分:0)
访客模式可能有所帮助。
http://en.wikipedia.org/wiki/Visitor_pattern
这个想法是将遍历逻辑(单步执行对象)与每个对象的处理分开。在这种情况下,每个对象的逻辑是序列化(编码)单个对象(当然还是反序列化)。使用普通的OOP技术,这应该是相当简单和最少重复的。
实现遍历和特定于访问者模式的代码很烦人,但它主要是样板文件,应该是一次性的。
答案 3 :(得分:0)
一位评论者写道:
如果你的意思是“有没有办法我可以编写一个模板来自动调用每个成员变量的方法?”,那么答案是否定的......
如果方法是析构函数,我的(稍微邪恶)反击是是的
#include <iostream>
using namespace std;
bool Enable = false;
template <typename T>
class DS : public T {
public:
~DS() {
if (Enable) T::Serialize();
}
};
class A {
protected:
void Serialize() { cout << "A" << endl; }
};
class B {
protected:
void Serialize() { cout << "B" << endl; }
};
typedef DS<A> DSA;
typedef DS<B> DSB;
class C {
protected:
void Serialize() { cout << "C" << endl; }
private:
DSA a;
DSB b;
};
typedef DS<C> DSC;
int
main()
{
DSC c;
{
DSC c_copy = c;
Enable = true;
}
Enable = false;
}
输出的顺序相反,因此要重建对象,您必须解析序列化数据并将每个已完成的对象推送到堆栈上。然后,复合对象将知道从堆栈中弹出多少个子节点。或者,当然,序列化可以转到中间结构。
另一个有趣的想法是在启动时使用这个hack 一次(创建并销毁一个特殊对象),其中来自析构函数的回调将创建描述原始对象的数据结构。
我还注意到隐式拷贝构造函数可能存在类似的滥用行为,并且可能按正向顺序...