Это шаблон дизайна, возвращающий это от сеттеров? - PullRequest
7 голосов
/ 16 ноября 2011

Есть ли имя для этого:

class A
{
   A* setA() 
   {
       //set a
       return this; 
   }
   A* setB()
   {
       //set b
       return this;
   }
};

, чтобы вы могли сделать что-то вроде этого:

A* a = new A;
a->setA()->setB();

Есть ли недостатки в использовании этого?Преимущества?

Ответы [ 5 ]

9 голосов
/ 16 ноября 2011

Он известен как цепочка методов ( ссылка на часто задаваемые вопросы ) и чаще используется со ссылками, а не с указателями.

Цепочка методов тесно связана с Имена именованных параметров ( Ссылка на часто задаваемые вопросы ), как я сейчас, после публикации первоначальной версии этого ответа, вижу, что Стив Джессоп обсуждает в своем ответе .Идиома NPI - это один простой способ предоставить большое количество аргументов по умолчанию, не усложняя вызовы конструктора.Например, это относится к программированию с графическим интерфейсом.

Одна потенциальная проблема с техникой цепочки методов - это когда вы хотите или должны применять идиому NPI для классов в иерархии наследования.Затем вы обнаружите, что C ++ не поддерживает ковариантные методы .Что это такое: когда вы позволяете своим глазам блуждать вверх или вниз по классам в цепочке наследования классов, то ковариантный метод - это метод, определение которого включает некоторый тип, который для вашего блуждающего глаза различается по специфике так же, как и класс, который он определилв.

Это примерно та же проблема, что и при определении метода clone, который имеет одинаковое текстовое определение во всех классах, но должен быть кропотливо повторен в каждом классе, чтобы получить правильные типы.

Решение этой проблемы сложно без языковой поддержки;похоже, это сложная проблема, своего рода конфликт с системой типов C ++.В моем блоге «Как сделать типизированные необязательные аргументы в C ++ 98» приведены ссылки на соответствующий исходный код для автоматизации генерации ковариантных определений и статью, которую я написал об этом в Dr. Dobbs Journal.Может быть, я еще вернусь к этому для C ++ 11, или когда-нибудь, потому что сложность и возможная хрупкость могут показаться более высокой стоимостью, чем она стоит…

Приветствия и hth.,

4 голосов
/ 16 ноября 2011

Я слышал, что раньше это называлось что-то вроде " метода ", но я бы не назвал это шаблоном проектирования. (Некоторые люди также говорят о реализации « плавного интерфейса » с использованием этого - хотя я никогда раньше не видел, чтобы это называлось так, но Мартин Фаулер , кажется, писал об этом некоторое время назад )

Делая это, вы не теряете много - вы всегда можете с радостью проигнорировать возвращаемый результат, если не хотите так его использовать.

Что касается того, стоит ли это делать, я менее уверен. Это может быть довольно загадочным в некоторых обстоятельствах. Однако в основном требуется для таких вещей, как operator<< для потокового ввода-вывода. Я бы сказал, что это призыв к тому, как он вписывается в остальную часть кода - это ожидаемо / очевидно для людей, читающих его?

(Как отметил Стив Джессоп, это почти всегда делается с помощью ссылок, а не указателей)

3 голосов
/ 16 ноября 2011

Другое распространенное использование - с «объектами параметров».Без объединения методов их довольно неудобно настраивать, но при этом они могут быть временными.

Вместо:

complicated_function(P1 param1 = default1, P2 param2 = default2, P3 param3 = default3);

Запись:

struct ComplicatedParams {
    P1 mparam1;
    P2 mparam2;
    P3 mparam3;
    ComplicatedParams() : mparam1(default1), mparam2(default2), mparam3(default3) {}
    ComplicatedParams &param1(P1 p) { mparam1 = p; return *this; }
    ComplicatedParams &param2(P2 p) { mparam2 = p; return *this; }
    ComplicatedParams &param3(P3 p) { mparam3 = p; return *this; }
};

complicated_function(const ComplicatedParams &params);

Теперь я могу назвать это:

complicated_function(ComplicatedParams().param2(foo).param1(bar));

Это означает, что вызывающая сторона не должна помнить порядок параметров.Без цепочки метода, который должен был бы быть:

ComplicatedParams params;
params.param1(foo);
params.param2(bar);
complicated_function(params);

Я также могу назвать его:

complicated_function(ComplicatedParams().param3(baz));

Что означает, что без определения тонны перегрузок я могу указать толькопоследний параметр и оставьте остальные по умолчанию.

Последний очевидный твик - сделать complicated_function членом ComplicatedParams:

struct ComplicatedAction {
    P1 mparam1;
    P2 mparam2;
    P3 mparam3;
    ComplicatedAction() : mparam1(default1), mparam2(default2), mparam3(default3) {}
    ComplicatedAction &param1(P1 p) { mparam1 = p; return *this; }
    ComplicatedAction &param2(P2 p) { mparam2 = p; return *this; }
    ComplicatedAction &param3(P3 p) { mparam3 = p; return *this; }
    run(void);
};

ComplicatedAction().param3(baz).run();
2 голосов
/ 16 ноября 2011

Недостатком является то, что если вы производите класс от A, скажите так:

class Foo : public A
{
public:
  Foo *setC()
  {
    // set C
    return this;
  }
};

тогда важен порядок, который вы называете сеттером. Сначала вам нужно будет вызвать все сеттеры на Foo: например, это не сработает:

Foo f=new Foo();
f->setA()->setC();

Принимая во внимание, что это будет:

Foo f=new Foo();
f->setC()->setA();
0 голосов
/ 16 ноября 2011

Он обычно используется, например, в Boost, но большую часть времени функции вместо этого возвращают ссылки:

A &setX()
{
    // ...
    return *this;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...