Вопрос

(Оставляя в стороне вопрос о том, есть ли они у вас вообще.)

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

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 () .

Это, очевидно, субъективно, и это просто мои личные предпочтения.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top