委托给子组件的模式

发布于 2024-10-03 06:29:51 字数 219 浏览 4 评论 0原文

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

例如,如果有一个类 st class A{B;C;D;} 那么A.Pack就会调用pack B、C、D 上的函数。

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

In the product I am working, one of very basic scenario is serialization of classes. Typically a class to be serialized calls serialization on its sub-component

e.g. if there is a class s.t. class
A{B;C;D;} then A.Pack will call pack
function on B,C,D.

Since there are many such classes, same pattern of code has to be duplicated over and over again.
Is it possible to encapsulate this behavior in a pattern (possibly using templates and inheritance)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

南街女流氓 2024-10-10 06:29:52

一位评论者写道:

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

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

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

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

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

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

One commenter wrote:

If you mean "is there a way I can write a template to automatically call a method on each of my member variables?", then the answer is no...

My (slightly evil) counter to that is yes, if the method is the destructor...

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

The output is in reverse order, so to reconstruct objects you'd have to parse the serialized data and push each completed object on a stack. Composite objects would then know how many children to pop off of the stack. Or, of course, the serialization could go to an intermediate structure.

Another intriguing idea would be to use this hack once at startup (create and destroy only one special object) where the callbacks from the destructors would create a data structure that described the original object.

I also note that implicit copy constructors have potential for similar abuse, and possible in forward order...

梦情居士 2024-10-10 06:29:51

使模板执行此操作的常用方法是使用类型列表:

#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,未指定任何行为。

The usual way of making a template do this is to use a type list:

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

Proper implementations of composites created from type lists give you accessors for the members and so on, but this is enough to show how they works, and prints out that it packed Fee, Fi, Fo and Fum without specifying any behaviour.

浮华 2024-10-10 06:29:51

有助于实现此目的的一种可能的设计是使用复合模式。您的组件(借用维基百科绘图)是可打包的,它将实现模板Pack()方法可以做类似这样的事情:

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

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

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

One possible design that would help accomplish this is to use the Composite pattern. Your Component (to borrow from the Wikipedia drawing) is Packable, which would implement a Template Method Pack() that can do something like so:

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

PackImpl() is a pure virtual method in Packable, and all classes that inherit implement it appropriately. GetChildren() would return an STL container (possibly empty), for iteration. It can be implemented in Packable, along with a private member collection to store the child objects. Basically, you then inherit all the classes from Packable, implement PackImpl(), and you're done.

Note that this will cause issues if your inheritance hierarchy depends on the child pieces being members directly. If you've approached the problem in terms of aggregation, this should work well.

⊕婉儿 2024-10-10 06:29:51

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

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

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

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

It's possible that the Visitor pattern may help.

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

The idea of this is to separate the traversal logic (stepping through your objects) from the handling of each object. In this case, the per-object logic is serializing (encoding) a single object (or deserializing, of course). This should be fairly simple and minimally repetitive using normal OOP techniques.

Implementing the traversal and the Visitor-pattern specific code is annoying, but it's mostly boilerplate and should be a one-off thing.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文