Когда оправдываются геттеры и сеттеры?

175

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

Когда получатели и установщики оправданы? Вы пытаетесь избежать их использования? Они чрезмерно используются в целом?

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

Источники для Критики Геттера / Сеттера (некоторые взяты из комментариев, чтобы дать им лучшую видимость):

Чтобы сформулировать критику просто: геттеры и сеттеры позволяют вам манипулировать внутренним состоянием объектов извне объекта. Это нарушает инкапсуляцию. Только сам объект должен заботиться о его внутреннем состоянии.

И пример процедурной версии кода.

struct Fridge
{
    int cheese;
}

void go_shopping(Fridge fridge)
{
     fridge.cheese += 5;
}

Мутаторная версия кода:

class Fridge
{
     int cheese;

     void set_cheese(int _cheese) { cheese = _cheese; }
     int get_cheese() { return cheese; }
 }

void go_shopping(Fridge fridge)
{
     fridge.set_cheese(fridge.get_cheese() + 5);        
}

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

Вопрос был ранее обсужден на переполнении стека:

Winston Ewert
источник
21
Getters and setters are often criticized as being not proper OO- Цитировать, пожалуйста.
Роберт Харви
45
@ Job, почему, потому что у него есть код? Это хороший вопрос и пахнет священной войной, если принять ее всерьез.
Питер Тернер
2
@Winston Ewert: «... вопрос в том, нужно ли вообще обращаться к данным внутри класса.»: Ну, никто не заставляет вас реализовывать методы получения и установки для всех переменных-членов, вы реализуете только те, которые вам нужны. Вот почему вы используете геттеры и сеттеры вместо открытых переменных-членов.
Джорджио
2
@Winston Ewert: Я не вижу, как отсутствие методов получения / установки решило бы эту проблему: должен быть способ получить доступ к каждому фрагменту данных, иначе это бесполезно; задача хорошего дизайна - решить, какая часть данных доступна для какой части кода. Если кто-то плохой дизайнер, он или она будет таким с или без добытчиков и сеттеров. Просто мои 2 цента.
Джорджио

Ответы:

162

Наличие геттеров и сеттеров само по себе не нарушает инкапсуляцию. Что нарушает инкапсуляцию, так это автоматическое добавление метода получения и установки для каждого элемента данных (каждое поле в языке Java), не задумываясь об этом. Хотя это лучше, чем обнародовать все элементы данных, это всего лишь маленький шаг.

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

  • Некоторые члены данных могут быть полностью внутренними по отношению к объекту и не должны иметь ни методов получения, ни установки.

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

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

  • Некоторые элементы данных могут быть изменены только определенным образом, например, увеличены или уменьшены на фиксированную величину. В этом случае вы бы указали метод increment()и / или decrement()метод, а не метод установки.

  • Тем не менее, другие могут фактически нуждаться в чтении-записи и иметь как геттер, так и сеттер.

Рассмотрим пример class Person. Допустим, у человека есть имя, номер социального страхования и возраст. Допустим, мы не позволяем людям когда-либо менять свои имена или номера социального страхования. Однако возраст человека должен увеличиваться на 1 каждый год. В этом случае вы бы предоставили конструктор, который бы инициализировал имя и SSN с заданными значениями, и который бы инициализировал возраст до 0. Вы также предоставили бы метод incrementAge(), который увеличил бы возраст на 1. Вы также предоставили бы добытчики для всех трех. В этом случае сеттеры не требуются.

В этом проекте вы разрешаете проверять состояние объекта извне класса и разрешаете его изменение извне класса. Однако вы не допускаете произвольного изменения состояния. Существует политика, которая фактически гласит, что имя и SSN не могут быть изменены вообще, и что возраст может быть увеличен на 1 год за один раз.

Теперь скажем, у человека тоже есть зарплата. И люди могут менять работу по желанию, что означает, что их зарплата также изменится. Для моделирования этой ситуации у нас нет другого пути, кроме как предоставить setSalary()метод! Разрешение на изменение зарплаты по желанию является вполне разумной политикой в ​​этом случае.

Кстати, в вашем примере, я дал бы класс и методы, вместо и . Тогда у вас все равно будет инкапсуляция.FridgeputCheese()takeCheese()get_cheese()set_cheese()


public class Fridge {
  private List objects;
  private Date warranty;

  /** How the warranty is stored internally is a detail. */
  public Fridge( Date warranty ) {
    // The Fridge can set its internal warranty, but it is not re-exposed.
    setWarranty( warranty );
  }

  /** Doesn't expose how the fridge knows it is empty. */
  public boolean isEmpty() {
    return getObjects().isEmpty();
  }

  /** When the fridge has no more room... */
  public boolean isFull() {
  }

  /** Answers whether the given object will fit. */
  public boolean canStore( Object o ) {
    boolean result = false;

    // Clients may not ask how much room remains in the fridge.
    if( o instanceof PhysicalObject ) {
      PhysicalObject po = (PhysicalObject)o;

      // How the fridge determines its remaining usable volume is a detail.
      // How a physical object determines whether it fits within a specified
      // volume is also a detail.
      result = po.isEnclosedBy( getUsableVolume() );
    }

     return result;
  }

  /** Doesn't expose how the fridge knows its warranty has expired. */
  public boolean isPastWarranty() {
    return getWarranty().before( new Date() );
  }

  /** Doesn't expose how objects are stored in the fridge. */
  public synchronized void store( Object o ) {
    validateExpiration( o );

    // Can the object fit?
    if( canStore( o ) ) {
      getObjects().add( o );
    }
    else {
      throw FridgeFullException( o );
    }
  }

  /** Doesn't expose how objects are removed from the fridge. */
  public synchronized void remove( Object o ) {
    if( !getObjects().contains( o ) ) {
      throw new ObjectNotFoundException( o );
    }

    getObjects().remove( o );

    validateExpiration( o );
  }

  /** Lazily initialized list, an implementation detail. */
  private synchronized List getObjects() {
    if( this.list == null ) { this.list = new List(); }
    return this.list;
  }

  /** How object expiration is determined is also a detail. */
  private void validateExpiration( Object o ) {
    // Objects can answer whether they have gone past a given
    // expiration date. How each object "knows" it has expired
    // is a detail. The Fridge might use a scanner and
    // items might have embedded RFID chips. It's a detail hidden
    // by proper encapsulation.
    if( o implements Expires && ((Expires)o).expiresBefore( today ) ) {
      throw new ExpiredObjectException( o );
    }
  }

  /** This creates a copy of the warranty for immutability purposes. */
  private void setWarranty( Date warranty ) {
    assert warranty != null;
    this.warranty = new Date( warranty.getTime() )
  }
}
Дейв Джарвис
источник
4
Пожалуйста, не принимайте мой пример с холодильником слишком серьезно. :) Настоящий холодильник должен быть контейнерным объектом, я ожидаю, что он будет знать, как удерживать объекты, не беспокоясь о том, что именно они были. IE это будет похоже на ArrayList. Что касается того, что делает его холодильником, скажем, он сериализует объекты на диск при сохранении, чтобы они выдержали системный сбой. Хорошо. Достаточно серьезно отнестись к моему примеру с холодильником.
Уинстон Эверт
6
Но разве холодильник не должен знать даты истечения срока годности, чтобы он мог сказать вам, что сыр испортился и его следует выбросить? :) Хорошо, мы должны это остановить! :)
Дима
10
Довольно интересная дискуссия о Fridge Logic, которую вы здесь проводите ...
Мейсон Уилер,
35
Хаха, я бы хотел увидеть объявление об этом: «Это совершенно новый вид холодильника: он бросает на тебя вещи, когда ты пытаешься взять что-то, чего там нет! Таким образом, ты попробуешь это только один раз! набейте холодильник, и пусть холодильник беспокоится о незаконном поведении! "
Габлин
42
В примере все неправильно. Не должно быть ни поля Age, ни метода setAge (). Возраст является функцией даты рождения человека по сравнению с некоторым моментом времени. Несмотря на кажущуюся простоту, это именно то, что не так с современной практикой полной изменчивости объектов и других плохих проектов, как это видно из методов get / set для закрытых полей, в отличие от тщательного рассмотрения того, что действительно изменчиво, что такое поле, что такое объект должен знать о его поведении и о том, какие изменения состояния действительно действительны (какие методы setX полностью уничтожают).
Даррелл Тиг
41

Основная причина получения и установки в Java очень проста:

  • Вы можете указывать только методы , а не поля в интерфейсе.

Следовательно, если вы хотите, чтобы поле проходило через интерфейс, вам понадобится ридер и метод записи. Они традиционно называются getX и setX для поля x.

user1249
источник
44
Это ограничение языка. Реальный вопрос заключается в том, должны ли мы позволять другим объектам манипулировать этим состоянием.
Уинстон Эверт
3
@Peter Turner, очевидно, ничто не мешает языку иметь интерфейсы, которые включают свойства. Под прикрытием, которое вполне может быть реализовано как метод получения / установки, но было бы достаточно легко добавить поддержку определений интерфейса для свойств.
Уинстон Эверт
2
@ Уинстон, в конечном итоге вам нужно будет разрешить классам передавать информацию друг другу, чтобы фактически выполнить работу. Что бы вы предложили вместо этого?
6
Предполагается, что объект должен обеспечивать более высокий уровень взаимодействия с внутренними объектами. Методы получения и установки имеют тенденцию быть интерфейсом низкого уровня. Например, предположим, у вас есть класс, который реализует двоичное дерево. У вас могут быть такие функции, как GetLeft (), GetRight (), GetValue () и GetKey () для навигации по дереву. Но это совершенно неправильный подход. Ваш класс бинарного дерева должен обеспечивать такие операции, как поиск, вставка, удаление.
Уинстон Эверт
6
Чтобы взять другой пример, рассмотрим кусок тетриса. Кусок тетриса имеет некоторое внутреннее состояние, например block_type, вращение, положение. Вы можете иметь методы получения, такие как GetBlockType (), GetRotation (), GetPosition (). Но ты действительно не должен. То, что вы должны иметь, - это метод GetSquares (), который возвращает список всех квадратов, занятых этой частью. У вас также не должно быть таких вещей, как SetPosition () или SetRotation (). Вместо этого у вас должны быть такие операции, как MoveLeft (), MoveRight (), Rotate (), Fall ().
Уинстон Эверт
20

С http://www.adam-bien.com/roller/abien/entry/encapsulation_violation_with_getters_and

Стиль JavaBean:

connection.setUser("dukie");
connection.setPwd("duke");
connection.initialize();

OO-стиль:

connection.connect("dukie","duke");

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

Ваш вопрос: когда оправдан метод получения / установки? Возможно, когда необходимо изменить режим или вам нужно опросить объект для получения какой-либо информации.

myObject.GetStatus();
myObject.SomeCapabilitySwitch = true;

Размышляя об этом, когда я впервые начал писать код на C #, я написал много кода в стиле Javabeans, показанном выше. Но когда я приобрел опыт работы с языком, я начал больше настраивать элементы в конструкторе и использовать методы, которые больше походили на описанный выше стиль ОО.

Роберт Харви
источник
6
Почему OO-стиль "дать все аргументы как параметры"?
5
В то время как ваш «OO-стиль» подходит для 2 или 3 вариантов, я бы предпочел стиль JavaBean, как только вы вышли выше этого. 20 перегруженных методов для каждой возможной комбинации параметров выглядят просто ужасно
TheLQ
1
@TheLQ: C # 4.0 делает это проще, разрешая необязательные и именованные параметры в вызовах конструктора и метода.
Роберт Харви
7
@TheLQ Вот для чего нужен Builder с беглым синтаксисом, см., Например , MapMaker от Guava .
Маартин
5
@ Thorbjørn Равн Андерсен, я бы сказал, что «давать все аргументы как параметры» лучше в ОО-стиле, чем «вызывать установщики JavaBean», потому что установщик подразумевает, что устанавливается базовый атрибут, который по определению раскрывает внутренние детали. Использование метода connect (..) в этом примере не делает такого предположения; может быть, вы установили атрибуты пользователя и пароля, а может и нет. Важно то, что вы сосредоточены на правильной концепции ОО: сообщение «подключиться».
Андрес Ф.
13

Когда получатели и установщики оправданы?

Когда поведение «get» и «set» фактически соответствует поведению в вашей модели, чего практически никогда не бывает .

Любое другое использование - это просто обман, потому что поведение бизнес-сферы не совсем понятно.

редактировать

Этот ответ мог показаться легкомысленным, поэтому позвольте мне расширить. Ответы, приведенные выше, в основном правильные, но сфокусированы на парадигмах программирования ОО-дизайна и некоторых, которые упускают общую картину. По моему опыту, это заставляет людей думать, что избегание получения и установки - это некое академическое правило для языков программирования ОО (например, люди думают, что замена методов получения на свойства - это великое дело)

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

Реальность такова, что очень маловероятно, что любая система в вашем доменном пространстве имеет геттеры и сеттеры. Вы не можете подойти к мужчине или женщине, которые отвечают за выплату заработной платы, и просто сказать «Установите эту зарплату на Х» или «Получите мне эту зарплату» . Такого поведения просто не существует

Если вы помещаете это в свой код, вы не проектируете свою систему так, чтобы она соответствовала модели вашего домена. Да, это нарушает интерфейсы и инкапсуляцию, но это не главное. Дело в том, что вы моделируете то, чего не существует.

Более того, вы, вероятно, упускаете важный шаг или процесс, потому что, возможно, есть причина, по которой я не могу просто подойти к зарплате и сказать, установить эту зарплату на X.

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

Кормак Мулхолл
источник
кажется, это не дает ничего существенного по сравнению с замечаниями, сделанными и объясненными в предыдущих 17 ответах
комнат
1
Другие ответы говорят об объектах, интерфейсах и других языковых парадигмах. Что хорошо, но это не центральная проблема. Геттеры и сеттеры не просто плохи, потому что они нарушают некоторые соглашения о кодировании, но в первую очередь потому, что вы вряд ли когда-либо сталкивались с таким поведением в реальной модели, которую вы представляете. Этот момент, как правило, теряется при обсуждении лучших практик кодирования.
Кормак Малхолл,
Что вы думаете об интерфейсе для расчета заработной платы, если это не set/getзарплата?
Уинстон Эверт
1
Если они обернуты вокруг слоев авторизации и ограничены определенными внешними объектами, они не являются получателями и установщиками. Я не говорю, что в отделе начисления заработной платы нет метода, который бы изменял зарплату, но этот метод должен соответствовать внутренним процессам, действующим в самой компании. Например, большинство компаний устанавливают зарплату на основании контракта с работниками, который утверждается менеджером. Если зарплата сотрудника изменяется, составляется новый контракт, и менеджер должен его подписать. Поэтому начисление заработной платы следует ожидать объект контракта и какой - либо объект авторизации для изменения зарплатных
Cormac Малхолл
3
Или, другими словами, существует большая разница между set_salary (new_salary, employee_id) и авторизованным_salary_update (employee_contract, authorising_manager). Процесс должен моделировать внутренний бизнес-процесс
Кормак Мулхолл,
11

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

Очевидно, есть исключения. В Java вам может понадобиться использовать интерфейсы. Стандартная библиотека Java предъявляет требования обратной совместимости настолько экстремальные, что перевешивают обычные показатели качества кода. Возможно даже, что вы на самом деле имеете дело с легендарным, но редким случаем, когда есть большая вероятность, что вы позже сможете заменить сохраненное поле на лету, не нарушая интерфейс. Но это исключения. Геттеры и сеттеры - это анти-паттерн, который нуждается в особом обосновании.

rwallace
источник
6
Получатель и установщик означают, что атрибут, а не поле, является частью интерфейса. Предположим, что getBirthDate () и SetBirthDate () принимают «гггг / мм / дд», но в поле сохраняется количество дней с 01.01.1000. Вы можете изменить это на 01/01/0001, и получатель и установщик сохранят интерфейс одинаковым. Тем более, что «public» означает публично передаваемый, переменные никогда не должны быть публичными; всегда используйте установщик для проверки входящего значения, обновления любых связанных полей, преобразования по мере необходимости и т. д. Попробуйте использовать получатель в случае изменения внутреннего хранилища.
Энди Кэнфилд
@ AndyCanfield, что немного убедительно, сказать, что public означает публичный трейлинг ... Это так, если вы плохо программируете. Если у вас есть установщик, и ему отправляется неправильное значение, тогда вы можете вызвать исключение, которое вы могли бы назвать уничтожением программы, но оно завершается с ошибкой. Также, если пользователь объекта может дать переменной значение, а cud - неправильный значение, то вы можете иметь это в виду и проверить it.u может сказать «ах, но мне нужно сделать только 1 тест, если я использую сеттер», но, возможно, ваш собственный код дает переменной неверное значение… так что сеттер может не уменьшите ваши tests.n с помощью тестов, ваша прога не "
разбита
3

является ли поле доступным напрямую или через метод, не очень важно.

Классовые инварианты (полезные) важны. И чтобы сохранить их, нам иногда не нужно что-то менять извне. Например. если у нас есть класс Square с раздельными шириной и высотой, изменение одного из них превращает его в нечто иное, чем квадрат. Поэтому нам нужен метод changeSide. Если бы это был Rectangle, у нас могло бы быть setters / public field. Но setter than проверяет, будет ли его значение больше нуля.

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

Кстати. Мне нравится использовать простые неизменяемые значения, содержащие классы с открытыми конечными полями.

user470365
источник
Это почти единственное, чего следует избегать. Переход от поля к свойству / получателю / сеттеру является переломным изменением в большинстве языков.
MrDosu
2

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

Джордж Сильва
источник
1
Я удивлен, что этот ответ так далеко внизу. Получатели и установщики позволяют вам делать больше в будущем (например, инициировать событие, проводить дополнительную проверку ввода, вести внутреннюю бухгалтерию), не нарушая существующий API. Даже с закрытым кодом меньше API-интерфейсов, которые вы должны изменить, меньше зависимостей для обновления и меньше ошибок, связанных с этими обновлениями.
AmadeusDrZaius
0

Мой подход заключается в следующем -

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

Если это структура POD, я оставляю слоты доступными.

На более абстрактном уровне вопрос заключается в том, «кто управляет данными», и это зависит от проекта.

Пол Натан
источник
0

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

Вот код из второго примера, написанного на Ruby:

class Fridge
  attr_accessor :cheese
end

def go_shopping fridge
  fridge.cheese += 5
end

Заметьте, это очень похоже на первый пример в Java? Когда геттеры и сеттеры рассматриваются как первоклассные граждане, их использование не является рутиной, и дополнительная гибкость иногда может быть настоящим благом - например, мы можем решить вернуть значение по умолчанию для сыра в новом холодильнике:

class Fridge
  attr_accessor :cheese

  def cheese
    @cheese || 0
  end
end

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

Тобиас Коэн
источник
Это прекрасно сказано. Функции конструктора были добавлены, потому что мы хотим создавать неизменяемые объекты при постепенном построении рецепта: возможно, если вы добавите искусственный подсластитель, вам не нужно добавлять сахар. Используя именованные параметры и даже с перегрузкой, очень сложно создать один метод, который делает все. И, конечно же, у java нет именованных параметров, поэтому есть еще одна причина, по которой люди используют шаблон Builder.
YoYo
0

Рассмотрим Sizeкласс, который инкапсулирует ширину и высоту. Я могу устранить сеттеры с помощью конструктора, но как это поможет мне нарисовать прямоугольник Size? Ширина и высота не являются внутренними данными для класса; это общие данные, которые должны быть доступны потребителям Size.

Объекты состоят из поведения и состояния - или атрибутов. Если нет открытого состояния, то только поведение является публичным.

Без состояния, как бы вы отсортировали коллекцию объектов? Как бы вы искали конкретный экземпляр объекта? Если вы используете только конструкторы, что произойдет, если у вашего объекта длинный список атрибутов?

Ни один параметр метода никогда не должен использоваться без проверки. Поэтому лень писать

setMyField(int myField){
    this.myField = myField;
}

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

Геттеры, сеттеры, свойства, мутаторы, называйте их как хотите, но они необходимы.

Дейл
источник
Но, по крайней мере, у вас есть непротиворечивый общедоступный интерфейс, который вы можете вернуть и ввести в правила проверки, не нарушая код ваших клиентов. Это не правда. Добавление правил проверки в будущем может очень «нарушить код ваших клиентов». Возьмите, intнапример, переменную экземпляра, и представьте, что вы решили добавить правило проверки, которое принимает только неотрицательные значения. Проблема в том, что код вашего клиента уже может полагаться на возможность установить его в отрицательное значение ...
jub0bs
0

Если геттеры и сеттеры нарушают инкапсуляцию и истинное ОО, у меня серьезные проблемы.

Я всегда чувствовал, что объект представляет собой то, что приходит на ум лучше всего, что вам нужно для этого.

Я только что закончил писать программу, которая генерирует Лабиринты на Java, и у меня есть класс, который представляет «Квадраты лабиринта». У меня есть данные в этом классе, которые представляют координаты, стены и логические значения и т. Д.

У меня должен быть какой-то способ изменить / манипулировать / получить доступ к этим данным! Что мне делать без геттеров и сеттеров? Java не использует свойства, и установка всех моих данных, которые являются локальными для этого класса, на общедоступные, является ОПРЕДЕЛЕННО нарушением инкапсуляции и ОО.

Брайан Харрингтон
источник
6
Вот вопрос: если бы вы сделали все эти поля общедоступными и перестали использовать методы получения и установки, как бы ваш код отличался?
Уинстон Эверт
В теории его нет. Зачем вообще делать уроки в этот момент? Тот же аргумент может быть сделан с чем угодно. Но пост ниже моего, кажется, сказал это более элегантно. (Получатели и установщики - это способ безопасно передать значение объекту или извлечь значение из этого объекта.) Пост продолжается ...
Брайан Харрингтон,
3
Я хотел бы отметить: этот плакат в конечном итоге согласился со мной и опубликовал другой ответ на этот счет. По сути, использование методов получения и установки равнозначно непосредственному управлению данными. Вы действительно не получаете OOness, делая это. Для того чтобы быть OO, вы должны предоставить интерфейс данных более высокого уровня.
Уинстон Эверт
4
Я думаю, что лучший способ описать это - сказать, что вы должны проектировать свои интерфейсы снаружи, а не изнутри. Интерфейс, предоставляемый вашим объектом, должен определяться тем, как объект используется, а не тем, как он реализован. Так что не пишите геттеры и позволяйте внешнему объекту интерпретировать данные. Узнайте, на какой вопрос им нужно ответить, и напишите метод, который отвечает на этот вопрос. Не позволяйте внешним объектам изменять состояние с помощью установщика, узнайте, какие манипуляции им нужны, и напишите методы, которые это делают.
Уинстон Эверт
2
В некоторых случаях, геттеры и сеттеры являются лучшим методом. Иногда это лучший интерфейс, который вы можете предоставить. Однако во многих случаях это не так. Во многих случаях вы просто просачиваете детали реализации в другие классы.
Уинстон Эверт
-1

Во-первых, в общих чертах есть два типа объектов, которые я буду комментировать, типы значений и сервисы.

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

Типы значений также должны быть неизменяемыми, с другой стороны, бывают случаи, когда это нецелесообразно, например, ORM или какое-либо другое отображение, например, из виджета в объект. Все другие типы значений, которые передаются по системе от одного слоя или части к другому, должны быть неизменяемыми и не должны иметь установщиков.

MP01
источник
Я бы предложил третий тип: изменяемый контейнер, цель которого - хранить одно или несколько значений; во многих случаях не следует ожидать, что у контейнера будет много логики или проверки. Код, которому нужен метод для вычисления шести натуральных чисел, может передать контейнер для шести целых чисел в метод, который будет хранить результаты в нем. Ответственность за обеспечение положительных чисел, как правило, должна лежать на вызывающем или вызываемом методе, а не на контейнере.
суперкат
-1

Метод получения / установки оправдан, как и любой другой публичный метод. Обоснование в основном потому, что мы хотим предоставить интерфейс с внешним миром, который определяет, как наш класс взаимодействует с другими классами. Мы делаем это главным образом потому, что хотим уменьшить связь между отдельными объектами. Уменьшить сцепление - хорошая вещь по многим причинам:

  • Я могу использовать из моего текущего кода тот же интерфейс класса, хотя в то же время этот класс добавил новые методы (но сохранил старый интерфейс)
  • Может изменить внутреннее представление класса, не влияя на его потребителей
  • Уменьшите вероятность ошибок: если вы сделаете свои внутренние данные частными, вы можете точно знать, что внешний код не будет связываться с вашими внутренними данными
Ghita
источник
-1

Да, геттеры и сеттеры являются анти-паттернами в объектно-ориентированном программировании и никогда не должны использоваться: http://www.yegor256.com/2014/09/16/getters-and-setters-are-evil.html . Короче говоря, они не вписываются в объектно-ориентированную парадигму, потому что они побуждают вас относиться к объекту как к структуре данных. Что является серьезным заблуждением.

yegor256
источник
2
кажется, это не дает ничего существенного по сравнению с замечаниями, сделанными и объясненными в предыдущих 18 ответах
комнат
4
Это также немного сильно, чтобы сказать никогда .
Уинстон Эверт
-4

Я надеюсь, что все, что IDE, разработанная компанией, которая разрабатывает ваш язык программирования автоматически, не нарушает «Принципы объектной ориентации».

Тем не менее, всякий раз, когда у вас есть общедоступная переменная в области видимости, используйте геттер и сеттер, и вы великолепны. Мне кажется, что это необходимый элемент чрезвычайно объектно-ориентированного принципа инкапсуляции, даже если он выглядит просто как много ненужного кода.

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

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

Питер Тернер
источник
4
и что именно я получил, добавив много кода для вызова setFoo () и getFoo () каждый раз, когда я имею дело с переменной?
Уинстон Эверт
7
Вот в чем дело, я не думаю, что вы получаете инкапсуляцию. Вы все еще манипулируете состоянием объекта из других мест. Вы просто делаете это более многословно. Чтобы получить инкапсуляцию, вам нужно централизовать манипулирование значениями в классе, которому принадлежит это значение. Все остальное - это просто процедурный код в ОО одежде. (В этом нет ничего плохого, но это то, что есть).
Уинстон Эверт
2
Преимущество инкапсуляции заключается в том, что вся логика, связанная с конкретными значениями, находится в одном месте (более или менее). Я не получаю это с геттерами и сеттерами. Следовательно, я не думаю, что получаю много пользы для них.
Уинстон Эверт
1
Тем не менее, я согласен, что я предпочел бы иметь код с геттерами и сеттерами, а не тот, который свободно обрабатывает данные через публичный доступ. Таким образом, я могу, по крайней мере, прикрепить проверку входящих данных.
Уинстон Эверт
3
Я, конечно, не фанат многословия. Но я не согласен с тем, что люди на самом деле делают свои переменные приватными, а затем вызывают их свободно, а в итоге получаются практически без разницы. Если вы хотите использовать геттеры / сеттеры в вашем классе, это нормально. Вопрос почему? Основное преимущество, о котором я знаю, это то, что вы можете провести некоторую проверку данных, чтобы обнаружить ошибки раньше. Это менее полезно, когда весь код, управляющий данными, находится в одном месте. Но это все еще может быть полезным. В идеале у вас есть язык, который поддерживает свойства и может избежать многословия.
Уинстон Эверт
-4

Я думал, что у нас будут более обычные ответы здесь?

Геттеры и сеттеры в целом чрезвычайно ООП (любой, кто говорит что-то еще, не прав, просто так).

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

Однако причина, по которой вы не делаете поля общедоступными, а вместо этого получаете геттеры и сеттеры, в основном:

  • Невозможно сделать правильные тесты, используя поля. Геттеры / сеттеры намного лучше в этом отношении.

  • Невозможно правильно использовать поля в режиме программирования в реальном времени. Синхронизированные методы получения / установки - путь.

  • Тема интерфейса (уже объяснил, поэтому не буду).

  • Это проще в использовании при правильном повторном использовании системы.

  • Гораздо сложнее узнать, какие классы используют ваш класс и что сломается, если вы измените поле, чем метод получения / установки.

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

Skarion
источник
2
+1 за проверяемость. Шокирован, что никто не упомянул об этом раньше. Еще одна полезная особенность свойств - их отладка. (гораздо проще вставить точку останова в коде, чем требовать аппаратные / памяти точки останова для полей).
Марк Н
9
Возражение против добытчиков и установщиков состоит в том, что они часто используются, чтобы следовать букве закона ООП, игнорируя при этом дух и намерение. ООП говорит, что вы не должны позволять другим объектам смешиваться с вашими внутренностями. Если вы определяете геттеры и сеттеры, которые в любом случае позволяют вам это делать, вы в конечном итоге нарушаете ООП.
Уинстон Эверт
6
Я согласен, что есть много преимуществ геттеров и сеттеров по сравнению с простыми публичными полями. Тем не менее, мне не ясно, насколько лучше проводить с ними тестирование. Могли бы вы объяснить?
Уинстон Эверт
4
@Skarion: я не согласен с вашим ответом. Похоже, вы предлагаете две альтернативы: либо использование методов получения / установки, либо обнародование полей. Но есть и третий вариант: не использовать ни геттеры / сеттеры, ни выставлять поля! Зачем вам нужно проверять состояние внутреннего поля? Разве вы не должны тестировать публичный API вашего объекта?
Андрес Ф.