Pull to refresh

Method chaining

Reading time2 min
Views24K
В этом посте я поговорю о простом, но иногда полезном приеме программирования — method chaining. Также расскажу про потенциальный подводный камень, связанный с его использованием

Тест


Чтобы было интереснее, сначала небольшой тест.

1. Что такое method chaining? В суть этого приема?

2. Как он реализуется в С++?

3. Сможете ли вы придумать потенциально опасную ситуацию, связанную с использованием этого приема?

Теория


Иногда, при использовании или написании больших классов возникает необходимость вызвать подряд несколько методов объекта этого класса. Обычно это выглядит так:

class worker
{
	public:
	void set_data(const data_t& d);
	void process();
	void send_result();
	void print_log();
	...
};

void foo()
{
	worker w;
	w.set_data(data_t{});
	w.process();
	w.send_result();
	w.print_log();
	...
}


Прием method chaining позволяет сократить этот код. Для этого мы в каждом нашем методе возвратим ссылку на наш объект и выстроим вызовы в цепочку.

class worker
{
	public:
	worker& set_data(const data_t& d){...; return *this;}
	worker& process(){...; return *this;}
	worker& send_result(){...; return *this;}
	worker& print_log(){...; return *this;}
	...
};

void foo()
{
	worker w;
	w.set_data(data_t{}).process().send_result().print_log();
	...
}

Насколько я знаю, такой прием любят в Java. В С++ он не пользуется особой популярностью и я ни в коем случае не призываю его использовать, но знать о нем, думаю, не помешает.

Подводный камень


Строго говоря, то что я опишу ниже относится не столько к method chaining, сколько к порядку вычисления аргументов и вызовов функций, но тем не менее при использовании “цепочки вызовов” эти правила на первый взгляд могут работать неожиданно. Итак.

struct worker 
{
    worker& process(int& i)
    {
        i = 185;
        return *this;
    }
    worker& print_result(const int& i)
    {
        std::cout <<"result: "<< i << std::endl;
        return *this; 
    }
};

int main()
{
    int data = 0;
    worker w;
    w.process(data).print_result(data+2);
}

Этот код скомпилируется без предупреждений и ошибок. Но результаты выполнения могут отличаться на разных компиляторах.
Дело в том, что хотя стандарт и гарантирует, что process() будет вызвана перед print_result(), но не гарантируется, что аргумент функции print_result будет вычислен после выполнения process(). Соответственно, иногда в результате выполнения этого кода может быть выведено “2”.
Tags:
Hubs:
+21
Comments37

Articles