стиль геттеров и сеттеров
-
05-07-2019 - |
Вопрос
(Оставляя в стороне вопрос о том, есть ли они у вас вообще.)
Я всегда предпочитал просто использовать перегрузку функций, чтобы дать вам одно и то же имя как для геттера, так и для сеттеров.
int rate() { return _rate; }
void rate(int value) { _rate = value; }
// instead of
int getRate() { return _rate; }
void setRate(int value) { _rate = value; }
// mainly because it allows me to write the much cleaner
total( period() * rate() );
// instead of
setTotal( getPeriod() * getRate() );
Естественно, я прав, но мне интересно, были ли у авторов библиотеки какие-то веские причины?
Решение
Я бы предпочел версии get / set, потому что это более понятно, что происходит. Если я увидел rate () и rate (10), как я узнаю, что rate (10) не просто использует 10 в расчете для возврата показателя? Я не знаю, так что теперь я должен начать искать, чтобы выяснить, что происходит. Одно имя функции должно делать одно, а не две противоположные вещи.
Кроме того, как уже отмечали другие, некоторые предпочитают опускать «get» и оставлять «set», т.е.
int Rate( );
void SetRate( int value );
Это соглашение также довольно ясно, у меня не будет проблем с его чтением.
Другие советы
Я всегда предпочитал опускать 'get' в моих методах получения, как и вы, с помощью rate ()
вместо getRate ()
. Но перегрузка для сеттера не кажется мне хорошей идеей, поскольку имя rate
не говорит о том, что объект мутирует. Рассмотрим:
total(period() * rate()); // awesome, very clear
rate(20); // Looks like it computes a rate, using '20'...but for what? And why ignore the return value?
Как насчет int rate ();
и void setRate (int value);
? Преимущество этого состоит в том, что две функции с одним и тем же именем не выполняют разные задачи, и все же допускает period () * rate ()
.
Несколько лет назад я бы полностью согласился. Совсем недавно начали появляться сомнения, потому что это делает неоднозначным восприятие адреса получателя или установщика. С такими инструментами, как tr1 :: bind, это действительно раздражает.
Например:
struct A
{
void Foo(int);
int Foo()const;
};
std::vector<A> v = ....;
std::vector<int> foos;
// Extract Foo
std::transform(
v.begin(), v.end(),
std::back_inserter(foos),
//Ambiguous
// std::tr1::bind(&A::Foo)
//must write this instead. Yuck!
std::tr1::bind(static_cast<int(Foo::*)()>(&A::Foo));
);
Оставляя в стороне вопрос о том, должны ли они вообще быть; -)
Я продолжу и упомяну, что это должен быть вопрос сообщества wiki.
Когда я начал изучать C ++, я искал руководства по стилю, и в некоторых моментах Google был хорош:
- Методы в верхнем регистре (это просто красивее).
- добытчики простые и малозначительные (
rate
). - указатели в явном виде и в нижнем регистре (
setRate
).
Быть кратким важно, но не за счет неполного или вводящего в заблуждение. По этой причине я предпочитаю GetFoo () и SetFoo () Foo () и Foo (int foo).
Существует несколько уровней "Получения" и "Настройки"
- Я использую Get и Set для "быстрых" операций.
- Если выполнение чего-то займет больше времени, то это часто будет Calc, поскольку эти названия подразумевают, что для получения результата необходимо выполнить некоторую работу.
- Для более длительных операций вы начинаете использовать такие префиксы, как Load / Save, Query / Store, Read / Write, Search / Find и т.д.
Таким образом, Get /Set может иметь полезное значение и быть частью более широкой, последовательной стратегии именования.
Хотя комментарий Эда верен, я предпочитаю реальные свойства над сеттером / геттером антипаттерна. Если 1/3 из методов в вашем доменном графе являются фиктивными, сгенерированными Eclipse, то что-то не так.
Однако я считаю, что без свойств первого класса антипаттерн имеет смысл.
Кроме того, это облегчает завершение кода.
obj.set (управляющая область сдвига)
для сеттеров
obj.get (управляющий сдвиг)
для получателей
Лично я считаю, что геттеры и сеттеры, найденные в парах, являются запахом кода, перенесенным из " visual " языки и их "свойства". В "хорошем" класс, члены данных доступны только для чтения или только для чтения, но не для чтения / записи.
Я думаю, что наиболее распространенной причиной получения и установки является недостаточно глубокий перенос объектной модели. В вашем примере, почему всего проходит период и тариф? Разве они не члены класса? Таким образом, вы должны только установить период и ставку, и вы должны только получить общую сумму. Р>
Возможно, есть исключения, но я просто ненавижу смотреть на класс и находить " getX / setX, getY / setY и т. д. и т. д. " Просто кажется, что было недостаточно продумано, как ДОЛЖЕН использоваться класс, и, скорее, автор сделал класс ЛЕГКИМ, чтобы получить доступ к данным, чтобы ему не пришлось задумываться над тем, как использовать класс.
Естественно, я прав.
Еще одна проблема, о которой никто не упоминал, - это перегрузка функций. Возьмите этот (надуманный и неполный) пример:
class Employee {
virtual int salary() { return salary_; }
virtual void salary(int newSalary) { salary_ = newSalary; }
};
class Contractor : public Employee {
virtual void salary(int newSalary) {
validateSalaryCap(newSalary);
Employee::salary(newSalary);
}
using Employee::salary; // Most developers will forget this
};
Без этого условия using
пользователи Contractor
не могут запросить зарплату из-за перегрузки. Недавно я добавил -Woverloaded-virtual
в набор предупреждений проекта, над которым я работаю, и вот, это проявилось повсюду.
Я применяю соглашение, в котором метод всегда должен быть глаголом, а класс всегда должен быть существительным (за исключением функторов по очевидным причинам). В этом случае префикс get / set должен использоваться для согласованности. Тем не менее, я также полностью согласен с Эдом Свангреном. Для меня это сводится к тому, что использование этих префиксов понятно.
Я предпочитаю избегать меток get и set, информация не нужна компилятору для выполнения большинства этих простых свойств.
у вас могут быть проблемы:
class Stuff {
void widget( int something ); // 'special' setter
const Widget& widget( int somethingelse ) const; // getter
}
Stuff a;
a.widget(1); // compiler won't know which widget you mean, not enough info
Если ваш метод получения просто rate ()
, ваш компилятор будет жаловаться, что это переопределение вашего другого символа rate
, если вы дали своему полю хорошее значимое имя, например тот. В этом случае вам нужно сделать что-то глупое, например, назвать вашего члена _rate
или другой подобный подход. Лично я ненавижу видеть / печатать эти подчеркивания, поэтому склоняюсь к подходу getRate ()
.
Это, очевидно, субъективно, и это просто мои личные предпочтения.