Сделал недавно простой шаблонный класс для упрощения решения маленькой задачи: есть константный объект, нужно выполнить над ним несколько шагов обработки, на каждом шаге возможна модификация части объекта. После всех шагов объект должен быть отослан другому агенту. Получалось что-то вроде:
void process_object( const some_object_t & original )
{
// Делаем копию, которая может модифицироваться.
some_object_t copy( original );
// На последующую обработку уходит уже копия объекта.
do_first_step_then_others( copy );
}
void do_first_step_then_other( some_object_t & obj )
{
// Обработка заключается в замене одного из полей,
// если его текущее значение не удовлетворяет
// некоторым условиям.
if( need_change_field_1( obj ) )
obj.set_field_1( value_for_field_1() );
do_second_step_then_others( obj );
}
...
void do_N_step_then_send( some_object_t & obj )
{
if( need_change_field_N( obj ) )
obj.set_field_N( value_for_field_N() );
send( obj );
}
При всей своей простоте это решение мне не нравилось тем, что копия объекта создается всегда. Даже если в 90% случаев объект вообще не модифицировался. А исходный объект был не очень маленьким – с десятком строковых полей + еще несколькими полями со сложной структурой.
Т.е. задача была в том, чтобы создавать копию объекта только при первой модификации. Но, если модификация произошла, чтобы именно копия объекта рассматривалась как актуальное значение. Для этого я написал очень простой класс (его шаблонный вариант приведен здесь). С его использованием преобразование объекта стало выглядеть приблизительно так:
void process_object( const some_object_t & original )
{
// Делаем "ленивую" копию, которая может модифицироваться.
mutable_copy_on_demand_t< some_object_t > copy( original );
// На последующую обработку уходит уже копия объекта.
do_first_step_then_others( copy );
}
void do_first_step_then_others(
mutable_copy_on_demand_t< some_object_t > & obj )
{
// Обработка заключается в замене одного из полей,
// если его текущее значение не удовлетворяет
// некоторым условиям.
if( need_change_field_1( obj.immutable() ) )
obj.changeable().set_field_1( value_for_field_1() );
do_second_step_then_others( obj );
}
...
void do_N_step_then_send(
mutable_copy_on_demand_t< some_object_t > & obj )
{
if( need_change_field_N( obj.immutable() ) )
obj.changeable().set_field_N( value_for_field_N() );
send( obj.immutable() );
}
Объект mutable_copy_on_demand_t хранит ссылку на исходный объект и указатель на копию. Изначально копии нет и метод immutable возвращает ссылку на исходную копию. При первом обращении к методу changeable создается копия, и метод immutable начинает возвращать ссылку на нее.
Вот такой частный случай Copy-On-Write, упрощенный за счет того, что ссылку на исходный объект не нужно контролировать – она в данном сценарии гарантированно остается неизменной.
2 комментария:
Re: А исходный объект был не очень маленьким – с десятком строковых полей + еще несколькими полями со сложной структурой.
SMS? :)
SMS? ;)
Ага. Но не сам по себе, а с туевой хучей сопроводительной информации.
Отправить комментарий