Можно ли отказаться от методов получения и установки простых классов? - PullRequest
19 голосов
/ 30 июня 2011

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

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

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

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

Ответы [ 6 ]

16 голосов
/ 30 июня 2011

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

Распространенные причины использования сеттера (возможно, есть и другие):

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

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

Редактировать : вопреки распространенному мнению, использование методов получения и установки вряд ли поможет вам изменить внутреннее представление класса, если эти изменения не являются незначительными. В частности, наличие сеттеров для отдельных членов делает это изменение очень трудным.

3 голосов
/ 30 июня 2011

Геттеры и сеттеры - действительно важный выбор дизайна, если они получают / устанавливают значение 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).
2 голосов
/ 30 июня 2011

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

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

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

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

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

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

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

Это нормальное исключение из хорошей практики?

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

0 голосов
/ 30 июня 2011

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

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

0 голосов
/ 30 июня 2011

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

  1. Может иметь любые возможные значения, например, int;
  2. Должен работать как встроенный тип при манипулировании его значением по соображениям производительности.

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

...