Подтвердить что ты не робот

Можно ли отказаться от геттеров и сеттеров для простых классов?

Я делаю очень простой класс для представления позиций в трехмерном пространстве.

В настоящее время я разрешаю пользователю напрямую получать и изменять индивидуальные значения X, Y и Z. Другими словами, они являются переменными public member.

template <typename NumericType = double>
struct Position
{
    NumericType X, Y, Z;

    // Constructors, operators and stuff...
};

Обоснование заключается в том, что, поскольку NumericType является параметром шаблона, я не могу полагаться на то, что есть достойный способ проверить значения для здравомыслия. (Как я узнаю, что пользователь не хочет, чтобы позиция была представлена ​​с отрицательными значениями?) Поэтому нет смысла добавлять геттеры или сеттеры для усложнения интерфейса, а прямой доступ должен быть одобрен для его краткости.

Pos.X = Pos.Y + Pos.Z; // Versus...
Pos.SetX(Pos.GetY() + Pos.GetZ());

Это хорошее исключение из хорошей практики? Будет ли (гипотетический) будущий сопровождающий моего кода искать меня и ударить меня в лицо?

4b9b3361

Ответ 1

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

Общие причины использования сеттера (возможно, больше):

  • Валидация: не все значения, разрешенные типом переменной, действительны для члена: перед назначением требуется проверка правильности.
  • Инварианты: возможно, потребуется отрегулировать зависимые поля (например, для повторной калибровки массива может потребоваться перераспределение, а не только для хранения нового размера).
  • Крючки: есть дополнительные работы для выполнения до/после назначения, такие как инициирование уведомлений (например, наблюдатели/слушатели зарегистрированы на значении).
  • Представление: поле не сохраняется в формате "опубликовано" в качестве геттеров и сеттеров. Поле может даже не храниться в самом объекте; значение может быть отправлено другому внутреннему элементу или сохранено в отдельных компонентах.

Если вы считаете, что ваш код никогда, никогда не будет использовать или не требует какого-либо из вышеперечисленных, то создание геттеров и сеттеров по принципу определенно не является хорошей практикой. Это просто приводит к раздуванию кода.

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

Ответ 2

Getters и seters действительно являются только важным выбором дизайна, если они получают/устанавливают значение abstract, которое вы могли бы реализовать любым количеством способов. Но если ваш класс настолько прямолинейный, и члены данных настолько фундаментальны, что вам нужно разоблачить их напрямую, тогда просто сделайте их общедоступными! Вы получаете хороший, дешевый агрегатный тип без излишеств и полностью самодокументируетесь.

Если вы действительно хотите, чтобы член данных был приватным, но все же предоставлял полный доступ к нему, просто запустите одну функцию доступа, перегруженную один раз, как T & access() и один раз как const T & access() const.


Изменить: В недавнем проекте я просто использовал кортежи для координат с глобальными функциями доступа. Возможно, это может быть полезно:

template <typename T>
inline T cX(const std::tuple<T,T,T> & t) { return std::get<0>(t); }

typedef std::tuple<double, double, double> coords;
//template <typename T> using coords = std::tuple<T,T,T>; // if I had GCC 4.8

coords c{1.2, -3.4, 5.6};

// Now we can access cX(c), cY(c), cZ(c).

Ответ 3

Понадобился немного времени, но я отследил это старое интервью Страуструпа, где он обсуждает структуры открытых данных и инкапсулированные классы: http://www.artima.com/intv/goldilocks3.html

В большей степени влияя на специфику, существуют измерения, которые могут отсутствовать/занижены в существующих ответах. Преимущества инкапсуляции увеличиваются с помощью:

  • перекомпиляция/зависимость ссылок: низкоуровневый библиотечный код, который используется большим количеством приложений, где эти приложения могут занять много времени и/или их сложно перекомпилировать и перераспределить
    • обычно проще, если реализация была вне очереди (для чего может потребоваться идиома pImpl и компрометация производительности), поэтому вам нужно только перезагрузить и еще проще, если вы можете развернуть новые общие библиотеки и просто отскочить от приложения
    • в отличие от этого, значительно меньше выгоды от инкапсуляции, если объект используется только в реализации "не extern" конкретной единицы перевода
  • стабильность интерфейса, несмотря на волатильность реализации: код, где реализация является более экспериментальной/изменчивой, но требование API хорошо понято
    • обратите внимание на то, что, будучи осторожным, можно предоставить прямой доступ к переменным-членам при использовании typedef для своих типов, чтобы можно было заменить прокси-объект и поддерживать идентичное использование клиентского кода при вызове другой реализации

Ответ 4

Хорошо для такой хорошо известной структуры, что:

  • Может иметь любые возможные значения, такие как int;
  • При работе с встроенным типом при работе с этим значением, по соображениям производительности,

Однако, если вам нужно больше, чем тип, который "просто является трехмерным вектором", тогда вы должны перенести его в другой класс как частный член, который затем будет выставлять x, y и z через функции-члены и элемент дополнительных функций функции.

Ответ 5

Если вы сделаете очень простой материал, ваше решение может быть прекрасным.

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

Ответ 6

Обоснование этого заключается в том, что, поскольку NumericType является параметром шаблона, я не могу полагаться на то, что существует достойный способ проверить значения для здравомыслия. (Как узнать, что пользователь не хочет, чтобы позиция была представлена ​​отрицательными значениями?)

Язык и компиляторы хорошо поддерживают этот случай (по специализации).

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

Спорный аргумент - см. выше.

Является ли это хорошим исключением из хорошей практики?

Я не думаю, что это так. Ваш вопрос предполагает, что валидация должна существовать, но не стоит выполнять/поддерживать, потому что вы решили использовать template в своей реализации и не специализируетесь на выбранной вами языковой функции. Благодаря такому интерфейсу интерфейс, как представляется, частично поддерживается - эти недостающие реализации просто загрязняют реализации клиентов.