2009-04-15 1 views

ответ

18

Это достаточно удобный образ, если есть много вещей, которые необходимо установить на объекте.

class Foo 
{ 
     int x, y, z; 
public: 
     Foo &SetX(int x_) { x = x_; return *this; } 
     Foo &SetY(int y_) { y = y_; return *this; } 
     Foo &SetZ(int z_) { z = z_; return *this; } 
}; 

int main() 
{ 
     Foo foo; 
     foo.SetX(1).SetY(2).SetZ(3); 
} 

Эта модель заменяет конструктор, который принимает три Интс:

int main() 
{ 
     Foo foo(1, 2, 3); // Less self-explanatory than the above version. 
} 

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

Для справки, более полный пример такого рода техники называется «Named Parameter Idiom» в C++ FAQ Lite.

Конечно, если вы используете это для именованных параметров, вы можете взглянуть на boost::parameter. Или вы не можете ...

+0

Я заинтригован этой техникой в ​​последнее время. Будет интересно посмотреть, есть ли у кого-нибудь какие-либо недостатки. –

+0

Интересно. Я не считал это альтернативой сложным конструкторам. Этот метод может сократить большое количество перегрузок конструктора. –

+1

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

0

Я бы так не подумал. Как правило, вы думаете об объекте «setter», как только это.

Кроме того, если вы только что установили объект, у вас нет указателя на него?

2

Не все сеттеры, но некоторые из них могут возвращать ссылку на объект, который будет полезен.

вид

a.SetValues(object)(2)(3)(5)("Hello")(1.4); 

Я использовал этот раз давно построить SQL построитель выражений, который обрабатывает все проблемы ускользает и другие вещи.

SqlBuilder builder; 

builder.select(column1)(column2)(column3). 
    where("=")(column1, value1) 
       (column2, value2). 
    where(">")(column3, 100). 
    from(table1)("table2")("table3"); 

Я не смог воспроизвести источники за 10 минут. Таким образом, реализация за шторами.

+0

Я сделал аналогичный материал для создания XML-документов на C++. Вопрос здесь, однако, кажется, касается общих свойств. –

10

Вы можете вернуть ссылку на this, если вы хотите, чтобы функция цепи сеттер вызовы вместе, как это:

obj.SetCount(10).SetName("Bob").SetColor(0x223344).SetWidth(35); 

Лично я считаю, что код труднее читать, чем альтернатива:

obj.SetCount(10); 
obj.SetName("Bob"); 
obj.SetColor(0x223344); 
obj.SetWidth(35); 
+0

Речь идет о макете. Вы можете просто написать первую часть, как вторую, просто опуская obj. на все, кроме первого и; на все, кроме последнего. ИМХО, это так же читаемо. –

2

Если ваша мотивация связана с цепочкой (например, предложение Брайана Энсинка), я бы предложил два комментария:

1. Если вы обнаружите, что часто настраиваете многие вещи одновременно, это может означать, что вы должны создать struct или class, который содержит все эти настройки, чтобы все они могли быть переданы сразу. Следующим шагом может быть использование этого или class в самом объекте ... но поскольку вы используете геттеры и сеттеры, решение о том, как его представлять внутри, будет всегда прозрачным для пользователей этого класса, поэтому это решение будет больше относятся к тому, насколько сложным является класс, чем что-либо.

2. Одна из альтернатив сеттера - это создание нового объекта, его изменение и его возврат. Это неэффективно и неприемлемо для большинства типов, особенно для изменяемых типов. Однако это вариант, который иногда забывают люди, несмотря на то, что он используется в классе строк многих языков.

2

Типичная цель этого стиля используется для построения объекта.

Person* pPerson = &(new Person())->setAge(34).setId(55).setName("Jack"); 

вместо

Person* pPerson = new Person(34, 55, "Jack"); 

Использование второй более традиционный стиль можно было бы забыть, если первое значение, передаваемое в конструктор был возраст или идентификатор? Это может также привести к созданию нескольких конструкторов, основанных на справедливости некоторых свойств.

Использование первого стиля позволяет забыть установить некоторые свойства объекта и может привести к ошибкам, когда объекты не «полностью» построены. (Свойство класса добавлено в более поздний момент, но не все места строительства были обновлены, чтобы вызвать требуемый сеттер.)

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

Эта модель может хорошо работать в приложениях, которые поддерживают их DataModel с течением времени в соответствии с правилами, аналогичных тем, которые используются во многих приложениях баз данных:

  • Вы можете добавить поле/атрибут таблицы/класс, который является NULL по умолчанию. (Таким образом, для обновления существующих данных требуется только новый столбец NULL в базе данных.)
  • Код, который не является изменением, должен по-прежнему работать так же, как и в этом поле NULL.
+0

+1 для безопасности во время компиляции. – Eclipse

+0

Ничего себе. Это потрясающая адаптация к отсутствию названных параметров C++ (например, Ada уже 20 лет). –

+0

-> нет. для указателей, хотя :) –

3

IMO сеттеры код запах, который обычно указывают на один из двух вещей:

Making A Mountian из кротовой норы

Если у вас есть класс вроде этого:

class Gizmo 
{ 
public: 
    void setA(int a) { a_ = a; } 
    int getA() const { return a_; } 

    void setB(const std::string & b) { v_ = b; } 
    std::string getB() const { return b_; } 
private: 
    std::string b_; 
    int a_; 
}; 

... и значения действительно такие простые, то почему бы не просто сделать пользователей данных общедоступными ?:

class Gizmo 
{ 
public: 
    std::string b_; 
    int a_; 
}; 

... Гораздо проще и, , если данные, которые просто вы ничего не теряете.

Другая возможность заключается в том, что вы могли бы быть

Изготовление кротовой норы из Mountian

Много раз данные не так просто: может быть, вы должны изменить несколько значений, сделать некоторые вычисления, уведомлять о другом объекте; кто знает что. Но если данные нетривиальны, то вам действительно нужны сборщики & геттеров, тогда нетривиальных достаточно для обработки ошибок. Таким образом, в этих случаях ваши приемники & должны возвращать какой-то код ошибки или делать что-то еще, чтобы указать, что произошло что-то плохое.

Если вы цепочки вызовов вместе, как это:

A.doA().doB().doC(); 

... и DOA() терпит неудачу, вы действительно хотите, чтобы называть DOB() и РОУ() в любом случае? Я сомневаюсь в этом.

+6

RE: "A.doA(). DoB(). DoC();" если doA терпит неудачу, для этого есть исключения. – Eclipse

+0

+1, хороший момент Джон. Также хороший момент Джош. –

Смежные вопросы