Шаблон для делегирования подкомпонента - PullRequest
2 голосов
/ 20 ноября 2010

В продукте, с которым я работаю, один из самых основных сценариев - сериализация классов. Обычно сериализуемый класс вызывает сериализацию для своего подкомпонента

например. если есть класс с.т. учебный класс A {B; C; D;} тогда A.Pack вызовет pack функция на B, C, D.

Поскольку существует много таких классов, один и тот же шаблон кода должен дублироваться снова и снова. Можно ли инкапсулировать это поведение в шаблон (возможно, с использованием шаблонов и наследования)

Ответы [ 4 ]

2 голосов
/ 21 ноября 2010

Обычный способ сделать шаблон для этого - использовать список типов:

#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 голосов
/ 20 ноября 2010

Один из возможных вариантов, который может помочь в этом, - это использование Composite pattern .Ваш компонент (позаимствованный из рисунка из Википедии) - Packable , который реализует Template Method Pack (), который может делать что-то вроде этого:

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

PackImpl () - это чисто виртуальный метод в Packable , и все классы, которые наследуют, реализуют его соответствующим образом.GetChildren () вернет контейнер STL (возможно, пустой) для итерации.Он может быть реализован в Packable вместе с частной коллекцией членов для хранения дочерних объектов.По сути, вы наследуете все классы от Packable , реализуете PackImpl (), и все готово.

Обратите внимание, что это вызовет проблемы, если ваша иерархия наследования зависит от того, какие дочерние элементы являютсяУчастники напрямую.Если вы подошли к проблеме с точки зрения агрегации, это должно сработать.

0 голосов
/ 20 ноября 2010

Один комментатор написал:

Если вы имеете в виду «есть ли способ, которым я могу написать шаблон для автоматического вызова метода для каждой из моих переменных-членов?», То ответ - нет ...

Мой (слегка злой) счетчик этого да, если метод - деструктор ...

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

Выходные данные находятся в обратном порядке, поэтому для восстановления объектов вам придется анализировать сериализованные данные и помещать каждый завершенный объект в стек. Затем составные объекты будут знать, сколько дочерних элементов выпадет из стека. Или, конечно, сериализация может перейти в промежуточную структуру.

Другая интригующая идея - использовать этот хак один раз при запуске (создать и уничтожить только один специальный объект), где обратные вызовы деструкторов создадут структуру данных, описывающую исходный объект.

Я также отмечаю, что неявные конструкторы копирования имеют потенциал для аналогичного злоупотребления и возможны в прямом порядке ...

0 голосов
/ 20 ноября 2010

Возможно, что шаблон Visitor может помочь.

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

Идея этого состоит в том, чтобы отделить логику обхода (пошаговое выполнение ваших объектов) от обработки каждого объекта. В этом случае логика для каждого объекта сериализует (кодирует) отдельный объект (или, конечно, десериализует). Это должно быть довольно простым и минимально повторяющимся при использовании обычных методов ООП.

Реализация кода обхода и кода, специфичного для шаблона посетителя, раздражает, но в основном это стандартный шаблон, который должен быть одноразовым.

...