Хорошо ли, чтобы все функции setter возвращали ссылку на объект в С++?

Хорошо ли, чтобы все функции setter вернули ссылку на объект в С++?

Ответ 1

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

 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.
 }

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

Для справки, более полный пример такого рода методов называется " Именованный идентификатор параметров" в С++ FAQ Lite.

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

Ответ 2

Вы можете вернуть ссылку на this, если хотите соединить вызовы функций сеттера вместе следующим образом:

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

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

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

Ответ 3

Установки ИМО - это запах кода, который обычно указывает одну из двух вещей:

Создание Горожанина из Мульхилла

Если у вас есть такой класс:

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_;
};

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

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

Создание мульхиллы из горы

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

Если вы соединяете вызовы вместе следующим образом:

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

... и doA() не удается, действительно ли вы хотите называть doB() и doC() в любом случае? Я в этом сомневаюсь.

Ответ 4

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

вид

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

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

SqlBuilder builder;

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

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

Ответ 5

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

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

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

Ответ 6

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

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

вместо

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

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

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

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

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

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

Ответ 8

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

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