[Из песочницы] Скрытая инициализация скрытых свойств объекта
Предположим у нас имеется класс обладающий богатым внутренним миром количеством свойств, которые необходимо инициализировать перед началом работы с объектом. Допустим также, что построение объекта должно осуществляться данными поступающими из вне (Например это могут быть DTO или строки XML/JSON формата из API библиотеки X).У меня, в таких ситуациях возникает желание вынести процесс адаптации и построения бизнес-объектов в отдельный слой моей системы, но появляются несколько вопросов:
Каким образом инициализировать приватные свойства класса если их много (желательно не нарушая принципов инкапсуляции)? Как реализовать несколько типов билдера из разных видов сырых данных? Задача, так сказать, вполне обычная, мало того рутинная. Ко всему прочему, есть много способов ее реализации, как правильных традиционных (телескопические конструкторы, посредством аксессоров), так и не очень. Для одного из таких способов нам вполне подойдет идиома pimpl. Почитать про нее можно тут и тут.(Исключительно для краткости, весь код будет уровня Hello world)
// data api version 1.0 struct api_msg_v1 { char value; }; // data api version 2.0 struct api_msg_v2 { int value; };
class object { public: //… void do_some_work () { //… } private: // … other properties int m_value; }; Итак, предположим нам требуется при создании экземпляра класса object инициализировать его свойство m_value данными содержащимися в любом из двух объектов api_msg_v1 и api_msg_v2.
Для этого в объявление класса object добавим шаблонный форвард, внутреннего класса который в последствии будет отвечать за создание и инициализацию наших объектов.
class object { public: /// forward builder template< typename T > struct builder; //… void do_some_work () { //… } private: // … other properties int m_value; }; В нашем случае важно то, что класс builder является внутренним, таким образом, он имеет доступ ко всей кухне класса верхнего уровня. Шаблонными мы его сделали для специализации по типам сообщений.
Теперь мы можем описать желаемую реализацию:
template<>
struct object: builder< api_msg_v1 >
{
static object *create_from (api_msg_v1 v) {
auto object = new object;
// init by api version 1…
object→m_value = static_cast
template<> struct object: builder< api_msg_v2 > { static object *create_from (api_msg_v2 v) { auto object = new object; // init by api version 2… object→m_value = v.value; return object; } };
//… auto c = object: builder< api_message_v2 >:: create_from (msg);
В итоге, мы получили вполне жизнеспособный способ создания и инициализации объектов. Конечно приведенный выше код сильно упрощенный, но думаю, что сама концепция вполне понятна.
Я уверен, что не открыл для вас Америку, но хотел напомнить, что любой язык дает 1001 способ выстрелить себе в ногу решить поставленную проблему.
Приятных выходных.