Хороший дизайн для делегирования функциональности шаблона-члена

У меня проблемы с поиском простого и элегантного дизайна для следующего сценария. Учебный класс Worker использует шаблон класса Helper сделать некоторую работу. В простом сценарии это выглядит так:

template<typename T>
class Helper {
  public:
    void Help(T data) : m_data(data) {} //NOTICE: Copy
    T const& Data() const { return m_data; }

    T m_data;
}

class SimpleWorker {
  public:
    SimpleWorker() : m_helper(SimpleData()) {} // non-temp used in reality
    void DoWork()
    {
        m_helper.help();
    }        

    Helper<SimpleData> m_helper;
}

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

template<typename T>
class Helper {
  public:
    Helper(T data) : m_data(data) {} //NOTICE: Copy
    T const& Data() const { return m_data; }

    T m_data;
}

class ComplexWorker {
  public:
    ComplexWorker() : m_helper(ComplexData()) {} // non-temp used in reality

    void DoWork()
    {
        m_helper.help();
        m_helper.GetData().DoSomethingComplexNotConst(); // <-------------
    }    

    Helper<ComplexData> m_helper;    
}

Очевидная проблема в том, что я не могу вызвать не константную функцию на Data() результат. Изготовление Data() неконстантность кажется плохой идеей Helper также используется в разных контекстах. Моя цель - найти элегантный способ изменить ComplexData используя свои функции-члены из ComplexWorkerComplexData необходимо изменить в первую очередь так, чтобы Helper можно продолжить работу с измененными данными.

РЕДАКТИРОВАТЬ: Изменено Helper конструкция для копирования предоставленных данных, чтобы лучше напоминать фактический поток

3 ответа

Я думаю лучше Helper иметь только статические функции, а не поддерживать состояние (так как вы создаете временные ComplexData() в ComplexWorker в вашем собственном коде). Передайте данные по ссылке или const-reference в зависимости от того, нужно ли вам изменить или нет.

// primary template
template<typename T>
class Helper {
public:
    static void help(T const& data) const {} // non-modifying
};

// specialization for ComplexData
template<>
class Helper<ComplexData> {
public:
    static void help(ComplexData const& data) const { } // non-modifying

    static void DoSomethingComplexNotConst(ComplexData& data) // modifying
    {
         // your implementation here
    }
};

class ComplexWorker {
public: 
    ComplexWorker() : m_data(ComplexData()) {} // create new data

    void DoWork()
    {
        Helper<ComplexData>::help(m_data);
        Helper<ComplexData>::DoSomethingComplexNotConst(m_data); // <--- now no problem
    }

   private:
       ComplexData m_data;         
};

Обратите внимание, что я сделал шаблон специализации для ComplexData, Существует некоторое дублирование кода в help() но вы можете легко извлечь это из обычной вспомогательной функции, не являющейся членом.

Сдается мне, что это зависит от того, что Helper на самом деле делает. Ваш пример просто дает конструктор и метод доступа, но я сомневаюсь, что это все, что он делает на практике.

Рассматривали ли вы просто использование наследования? Ваш Helper шаблон будет выглядеть так:

template<typename T>
class Helper : public T {
    Helper(T data) : T(data) {}
    void Help() {};
}

В этом случае вы можете использовать Helper<ComplexData> Объект непосредственно в отношениях "is-a":

class ComplexWorker {
    Helper<ComplexData> m_helper;

    void DoWork()
    {
        m_helper.help();
        m_helper.DoSomethingComplexNotConst();
    }        
}

Почему бы не обратиться к реализации Контейнерной части в STL. Перегрузка функции Data() может привести к балансу между безопасностью и элегантностью.

template <typename T>
class Helper {
public:
    Helper(T data) : m_data(data) {} //NOTICE: Copy
    T const& Data() const { return m_data; }
    T& Data() {return m_data; }
private:
    T m_data;
}

class ComplexWorker {
public:
    ComplexWorker() : m_helper(ComplexData()) {} // non-temp used in reality

    void DoWork()
    {
        m_helper.help();
        ComplexData &cd1 = m_helper.Data();
        cd1.QuerySth();
        const ComplexData &cd2 = m_helper.Data();
        cd2.ModifySth();
    }    
private:
    Helper<ComplexData> m_helper;    
}
Другие вопросы по тегам