Хорошо ли, чтобы все функции 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.
Ответ 7
Этот метод используется в Именованный параметр Idiom.
Ответ 8
Я бы так не подумал. Как правило, вы думаете об объекте "setter", как только это.
Кроме того, если вы просто установите объект, у вас нет указателя на него?