Когда оправданы геттеры и сеттеры

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

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

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

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

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

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

  struct Холодильник
{
    int cheese;
}

void go_shopping (холодильник с холодильником)
{
     fridge.cheese + = 5;
}
 

Версия для мутаторов кода:

  class Холодильник
{
     int cheese;

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

void go_shopping (холодильник с холодильником)
{
     fridge.set_cheese (fridge.get_cheese () + 5);
}
 

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

Ранее обсуждался вопрос о переполнении стека:

164 голоса | спросил 12 revs, 5 users 48%
Winston Ewert
1 Jam1000000amThu, 01 Jan 1970 03:00:00 +030070 1970, 03:00:00

16 ответов


154

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

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

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

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

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

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

  • В то же время другим может потребоваться чтение и запись, и они будут иметь как getter и сеттер.

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

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

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

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


  открытый класс Холодильник {
  частные объекты списка;
  частная гарантия даты;

  /** Как хранится внутренняя гарантия, деталь. * /
  общественный холодильник (гарантия даты) {
    //Холодильник может установить свою внутреннюю гарантию, но она не повторно открывается.
    setWarranty (гарантия);
  }

  /** Не раскрывает, как холодильник знает, что он пуст. * /
  public boolean isEmpty () {
    return getObjects (). isEmpty ();
  }

  /** Когда в холодильнике больше нет комнаты ... * /
  public boolean isFull () {
  }

  /** Ответит, будет ли данный объект соответствовать. * /
  public boolean canStore (Object o) {
    boolean result = false;

    //Клиенты могут не спрашивать, сколько осталось комнат в холодильнике.
    if (o instanceof PhysicalObject) {
      PhysicalObject po = (PhysicalObject) o;

      //Как холодильник определяет его оставшийся полезный том - деталь.
      //Как физический объект определяет, соответствует ли он указанному
      //том также является детальностью.
      result = po.isEnclosedBy (getUsableVolume ());
    }

     результат возврата;
  }

  /** Не раскрывает, как холодильник знает, что его гарантия истекла. * /
  public boolean isPastWarranty () {
    return getWarranty (). before (новая дата ());
  }

  /** Не показывает, как объекты хранятся в холодильнике. * /
  общедоступный синхронизированный хранилище void (Object o) {
    validateExpiration (o);

    //Может ли объект соответствовать?
    if (canStore (o)) {
      getObjects (). add (o);
    }
    else {
      throw FridgeFullException (o);
    }
  }

  /** Не раскрывает, как объекты удаляются из холодильника. * /
  public synchronized void remove (Object o) {
    if (! getObjects () содержит (o)) {
      throw new ObjectNotFoundException (o);
    }

    getObjects (). remove (o);

    validateExpiration (o);
  }

  /** Леновый инициализированный список, деталь реализации. * /
  private synchronized Список getObjects () {
    if (this.list == null) {this.list = new List (); }
    return this.list;
  }

  /** Как определяется срок действия объекта, это также деталь. * /
  private void validateExpiration (Object o){
    //Объекты могут ответить, прошли ли они мимо данного
    //срок годности. Как каждый объект «знает», он истек
    //- деталь. Холодильник может использовать сканер и
    //элементы могут иметь встроенные RFID-чипы. Это скрытая деталь
    //путем правильной инкапсуляции.
    если (o реализует Expires & amp; & amp; ((Истекает) o) .expiresBefore (сегодня)) {
      throw new ExpiredObjectException (o);
    }
  }

  /** Это создает копию гарантии для непреложных целей. * /
  частное void setWarranty (гарантия даты) {
    assert warranty! = null;
    this.warranty = новая дата (warranty.getTime ())
  }
}
 
ответил MaR 20 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowThu, 20 Sep 2012 21:52:29 +0400 2012, 21:52:29
40

Основная причина для getters и seters в Java очень проста:

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

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

ответил MaR 20 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowThu, 20 Sep 2012 21:52:29 +0400 2012, 21:52:29
19

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

Стиль JavaBean:

  connection.setUser ( "Dukie");
connection.setPwd ( "Герцог");
connection.initialize ();
 

OO-стиль:

  connection.connect ( "Dukie", "Герцог");
 

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

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

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

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

ответил MaR 20 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowThu, 20 Sep 2012 21:52:29 +0400 2012, 21:52:29
10

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

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

ответил MaR 20 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowThu, 20 Sep 2012 21:52:29 +0400 2012, 21:52:29
8
  

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

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

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

<сильный> Edit

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

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

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

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

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

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

ответил MaR 20 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowThu, 20 Sep 2012 21:52:29 +0400 2012, 21:52:29
3

действительно ли поле доступно напрямую или с помощью метода не важно.

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

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

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

ответил MaR 20 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowThu, 20 Sep 2012 21:52:29 +0400 2012, 21:52:29
2

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

ответил MaR 20 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowThu, 20 Sep 2012 21:52:29 +0400 2012, 21:52:29
0

Мой подход -

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

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

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

ответил MaR 20 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowThu, 20 Sep 2012 21:52:29 +0400 2012, 21:52:29
0

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

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

  class Холодильник
  attr_accessor: сыр
конец

def go_shopping холодильник
  fridge.cheese + = 5
конец
 

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

  class Холодильник
  attr_accessor: сыр

  сырой сыр
    @cheese || 0
  конец
конец
 

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

ответил MaR 20 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowThu, 20 Sep 2012 21:52:29 +0400 2012, 21:52:29
0

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

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

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

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

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

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

Getters, сеттеры, свойства, мутаторы, назовите их, что вам нужно, но они необходимы.

ответил MaR 20 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowThu, 20 Sep 2012 21:52:29 +0400 2012, 21:52:29
0

Если getters и seters нарушают инкапсуляцию и истинное OO, тогда я серьезно в беде.

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

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

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

ответил MaR 20 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowThu, 20 Sep 2012 21:52:29 +0400 2012, 21:52:29
-1

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

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

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

ответил MaR 20 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowThu, 20 Sep 2012 21:52:29 +0400 2012, 21:52:29
-1

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

  • Я могу использовать из моего текущего кода тот же интерфейс класса, хотя между тем класс добавил новые методы (но сохранил старый интерфейс)
  • Может изменять внутреннее представление класса, не затрагивая его потребителей.
  • Уменьшите вероятность ошибок: если вы сделаете свои внутренние данные конфиденциальными, вы можете точно знать, что внешний код не будет вмешиваться в ваши внутренние данные.
ответил MaR 20 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowThu, 20 Sep 2012 21:52:29 +0400 2012, 21:52:29
-1

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

ответил MaR 20 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowThu, 20 Sep 2012 21:52:29 +0400 2012, 21:52:29
-4

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

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

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

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

ответил MaR 20 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowThu, 20 Sep 2012 21:52:29 +0400 2012, 21:52:29
-4

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

Getters и seters, в общем, чрезвычайно OOP (любой, кто говорит что-то еще, ошибается, просто так).

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

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

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

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

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

  • Легче использовать при правильном повторном использовании системы.

  • Намного сложнее узнать, какие классы используют ваш класс и что сломается, если вы измените поле, чем getter /setter.

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

ответил MaR 20 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowThu, 20 Sep 2012 21:52:29 +0400 2012, 21:52:29

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132