Хороший дизайн для делегирования функциональности шаблона-члена
У меня проблемы с поиском простого и элегантного дизайна для следующего сценария. Учебный класс 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
используя свои функции-члены из ComplexWorker
ComplexData
необходимо изменить в первую очередь так, чтобы 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;
}