【问题标题】:Pattern for delegation to sub-component委托给子组件的模式
【发布时间】:2011-05-12 21:35:23
【问题描述】:

在我正在工作的产品中,非常基本的场景之一是类的序列化。通常要序列化的类在其子组件上调用序列化

例如如果有类 s.t.班级 A{B;C;D;} 然后 A.Pack 将调用 pack B、C、D 上的函数。

由于有很多这样的类,因此必须一遍又一遍地重复相同的代码模式。 是否可以将这种行为封装在一个模式中(可能使用模板和继承)

【问题讨论】:

  • 如果你的意思是“有没有办法我可以编写一个模板来自动调用我的每个成员变量的方法?”,那么答案是否定的......
  • 据我所知,您是正确的,但我不希望有人有更好的主意:-)

标签: c++ design-patterns templates serialization delegation


【解决方案1】:

制作模板的常用方法是使用类型列表:

#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】:

一种可能有助于实现此目的的设计是使用Composite pattern。您的组件(借用 Wikipedia 绘图)是 Packable,它将实现一个 Template Method Pack() 可以执行以下操作:

GetChildren();
    for each child:
        child.Pack()
PackImpl();

PackImpl() 是Packable 中的纯虚方法,所有继承的类都相应地实现了它。 GetChildren() 将返回一个 STL 容器(可能为空),用于迭代。它可以在 Packable 中实现,以及用于存储子对象的私有成员集合。基本上,然后你从 Packable 继承所有的类,实现 PackImpl(),你就完成了。

请注意,如果您的继承层次结构直接依赖于作为成员的子片段,这将导致问题。如果您已经从聚合的角度解决了这个问题,那么这应该会很好。

【讨论】:

  • 我知道我在这里很贪心,但是否有某种方式可以避免每次都重新实现 GetChildren,因为每次行为都是相同的(因为确切的孩子会有所不同)。否则似乎是一个很好的解决方案。
  • 已编辑 - 我没有正确描述 Composite,但这确实解决了您的问题。
【解决方案3】:

一位评论者写道:

如果您的意思是“我可以编写一个模板来自动调用我的每个成员变量的方法吗?”,那么答案是否定的......

我的(有点邪恶的)反驳是是的,如果方法是析构函数......

#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;
}

输出顺序相反,因此要重建对象,您必须解析序列化数据并将每个已完成的对象推入堆栈。然后复合对象会知道要从堆栈中弹出多少子对象。或者,当然,序列化可以转到中间结构。

另一个有趣的想法是在启动时使用这个技巧一次(只创建和销毁一个特殊对象),其中来自析构函数的回调将创建一个描述原始对象的数据结构。 p>

我还注意到,隐式复制构造函数有类似滥用的可能性,并且可能以正向顺序...

【讨论】:

    【解决方案4】:

    访客模式可能会有所帮助。

    http://en.wikipedia.org/wiki/Visitor_pattern

    这样做的想法是将遍历逻辑(逐步通过您的对象)与每个对象的处理分开。在这种情况下,每个对象的逻辑正在序列化(编码)单个对象(或者反序列化,当然)。使用普通的 OOP 技术,这应该相当简单且重复性极低。

    实现遍历和特定于访问者模式的代码很烦人,但它主要是样板文件,应该是一次性的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-10-20
      • 1970-01-01
      • 1970-01-01
      • 2015-09-13
      • 2013-05-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多