Зачем использовать геттеры и сеттеры / аксессоры?

avatar
Dean J
14 октября 2009 в 18:20
442873
41
1676

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

Если геттеры и сеттеры когда-либо делают больше, чем просто получение / установка, я могу понять это очень быстро, но я не на 100% понимаю, как:

public String foo;

хуже, чем:

private String foo;
public void setFoo(String foo) { this.foo = foo; }
public String getFoo() { return foo; }

Тогда как первый требует намного меньше шаблонного кода.

Источник
Asaph
14 октября 2009 в 18:26
6

@Dean J: Дублируйте со многими другими вопросами: coderhelper.com/search?q=getters+setters

Tordek
14 октября 2009 в 18:29
8

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

OMG Ponies
14 октября 2009 в 18:45
30

Гугл "соучастники зла"

Christian Hayter
14 октября 2009 в 19:10
37

«Аксессоры - зло», если вы пишете функциональный код или неизменяемые объекты. Если вы пишете изменяемые объекты с сохранением состояния, они очень важны.

Dave Jarvis
14 октября 2009 в 22:18
19

Скажи, не спрашивай. pragprog.com/articles/tell-dont-ask

OscarRyz
28 октября 2009 в 17:54
2

Осмелюсь сказать, что за исключением 6 и 8 ни один из этих пунктов не применяется в 99% случаев. Тем не менее, я бы хотел, чтобы java использовала String s = employee.name() и employee.name("oscar") вместо getName () setName ()

Dean J
29 октября 2009 в 14:28
1

@Oscar Reyes, да, я согласен с тем, что 6 и 8 - самые распространенные два из моего опыта. Тем не менее, я по-прежнему предпочитаю не использовать синтаксис C # ish; мой старик мозг запутался.

Prasanth
23 августа 2012 в 19:45
2

Что ж, я удивлен, что никто не говорит о синхронизации данных. Предположим, вы используете public String foo;, это не потокобезопасно! Вместо этого вы можете определить элементы get ter set с методами синхронизации, чтобы избежать неверности данных [я имею в виду, что другой поток испортил foo]. Я чувствовал, что об этом стоит упомянуть.

sbi
24 августа 2012 в 09:23
2

@goldenparrot: Чаще, чем никогда, синхронизация одиночных обращений к объекту оказывается неэффективной или даже склонной к проблемам синхронизации (когда вам нужно изменить сразу несколько свойств объекта ). IME, вам часто нужно обернуть операции в целом, которые осуществляют множественный доступ к объекту. Этого можно добиться только путем синхронизации на стороне пользователей типа.

Keith Pinson
25 марта 2013 в 16:55
1

Не объединяйте ваши любимые части всех ответов в сам вопрос. Это сайт вопросов и ответов, а не форум.

Timo Huovinen
10 августа 2013 в 08:53
3

@ErikReppen, что я должен прочитать, чтобы понять ООП в первую очередь?

Timo Huovinen
10 августа 2013 в 09:40
0

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

Timo Huovinen
13 августа 2013 в 08:29
0

@ErikReppen Спасибо за ссылку, я работаю над пониманием того, что должно быть ООП, но это оказывается трудным из-за количества распространяющейся дезинформации. P.S. Я также считаю, что Википедия - один из лучших источников во многих областях науки.

Erik Reppen
13 августа 2013 в 21:56
3

@TimoHuovinen - все дело в том, чтобы не выяснять, какая из 500 функций на самом деле изменяет значение. Вы делаете ценность или набор ценностей чем-то ответственным, а не чем-то, что нужно помещать на "американские горки" парам / возвратов. Избегайте ванильных геттеров / сеттеров и общедоступных свойств, и вам часто придется остановиться и подумать о том, как смоделировать свое приложение таким образом, чтобы его было легче читать, изменять, повторно использовать большие части, отлаживать и иметь уверенность. в без проверки bejeezus из него. Java и C # превзошли ООП, а затем отказались от него, ИМО.

AgilePro
28 октября 2014 в 00:47
1

Другая статья пытается дать рекомендации о том, где и когда использовать геттеры и сеттеры: Общедоступные или частные переменные-члены? Принцип YAGNI направляет нас не добавлять конструкции, пока мы действительно не узнаем, что они нам понадобятся.

Agi Hammerthief
18 декабря 2014 в 21:13
0

Пункты 2, 3 (?), 4, 5 и 8: предварительно да. Остальные, однозначно нет. Почему имеет значение, использовали ли вы Python, C #, Ruby, Objective-C или esolang, если ваш код простой, элегантный и легко обслуживаемый / расширяемый?

sitilge
9 сентября 2015 в 18:48
2

666 голосов за не кажутся совпадением

Kaiserludi
30 октября 2015 в 13:19
2

«Люди могут легко сказать, что вы не использовали Python». на самом деле должно быть Nr. 1!

Joschua
4 августа 2016 в 19:40
0

@Kaiserludi Не могли бы вы проиллюстрировать, как это хорошо?

Joschua
4 августа 2016 в 19:45
0

@DeanJ Не могли бы вы объяснить номер 10?

Kaiserludi
4 августа 2016 в 19:57
0

@Joshua: Я просто шутил, потому что автор явно несерьезно добавлял его в список.

Steve Hollasch
20 сентября 2016 в 17:53
2

11. Это полезные места для установки точек останова при отладке состояния, полученного / измененного из различных мест.

VGR
15 марта 2017 в 03:23
1

Недавний вопрос напомнил мне еще об одной причине для set-методов: возможность инициировать изменение свойства .

Ответы (41)

avatar
LBushkin
14 октября 2009 в 18:47
1090

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

Вот некоторые из известных мне причин:

  • Инкапсуляция поведения, связанного с получением или установкой свойства - это позволяет более легко добавлять дополнительные функции (например, проверку) позже.
  • Скрытие внутреннего представления свойства при раскрытии свойства с использованием альтернативного представления.
  • Изоляция общедоступного интерфейса от изменений - позволяя общедоступному интерфейсу оставаться неизменным, пока реализация изменяется, не затрагивая существующих потребителей.
  • Управление семантикой срока службы и управления (удаления) памяти свойства - особенно важно в средах с неуправляемой памятью (например, C ++ или Objective-C).
  • Обеспечение точки перехвата отладки, когда свойство изменяется во время выполнения - отладка, когда и где свойство изменилось на определенное значение, может быть довольно сложной без этого на некоторых языках.
  • Улучшенная совместимость с библиотеками, которые предназначены для работы со средствами получения / установки свойств - на ум приходят Mocking, Serialization и WPF.
  • Разрешение наследникам изменять семантику поведения и отображения свойства путем переопределения методов получения / установки.
  • Разрешение передачи геттера / сеттера в виде лямбда-выражений, а не значений.
  • Геттеры и сеттеры могут разрешать разные уровни доступа - например, получение может быть общедоступным, но набор может быть защищен.
avatar
user982042
8 июня 2021 в 05:38
1

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

Я думаю, что ООП усложняет ситуацию, поскольку программа растет, масштабирование для разработчика становится кошмаром.

Простой пример; мы генерируем заголовки c ++ из xml. Заголовок содержит простое поле, не требующее проверки. Но все же, поскольку в моде аксессуар OOPS, мы генерируем их следующим образом.

const Filed& getfield() const
Field& getField() 
void setfield(const Field& field){...} 

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

struct 
{
   Field field;
};

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

avatar
aditya lath
30 марта 2021 в 14:25
1

Существует разница между DataStructure и Object.

Структура данных должна раскрывать свое внутреннее состояние, а не поведение.

Объект не должен открывать свои внутренности, но он должен раскрывать свое поведение, которое также известно как Закон Деметры

В основном DTO считаются структурой данных, а не объектом. Они должны раскрывать только свои данные, а не поведение. Наличие Setter / Getter в DataStructure раскрывает поведение, а не данные внутри него. Это дополнительно увеличивает вероятность нарушения Закона Деметры .

Дядя Боб в своей книге «Чистый код» объяснил Закон Деметры.

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

Точнее, Закон Деметры гласит, что метод f класса C следует вызывать только следующие методы:

  • С
  • Объект, созданный f
  • Объект, переданный в качестве аргумента функции f
  • Объект, содержащийся в переменной экземпляра C

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

Итак, в соответствии с этим, пример нарушения LoD:

final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();

Здесь функция должна вызывать метод своего непосредственного друга, который здесь ctxt. Она не должна вызывать метод своего непосредственного друга друга. но это правило не распространяется на структуру данных. так что здесь, если ctxt, option, scratchDir являются структурой данных, тогда зачем оборачивать их внутренние данные некоторым поведением и нарушением LoD.

Вместо этого мы можем сделать что-то вроде этого.

final String outputDir = ctxt.options.scratchDir.absolutePath;

Это соответствует нашим требованиям и даже не нарушает LoD.

На основе чистого кода Роберта К. Мартина (дядя Боб)

avatar
Lakindu Hewawasam
19 ноября 2020 в 02:25
0

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

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

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

private String foo;
public void setFoo(String foo) { this.foo = foo; }
private String getFoo() { return foo; }

Ниже показана переменная только для чтения.

private String foo;
private void setFoo(String foo) { this.foo = foo; }
public String getFoo() { return foo; }
avatar
Blasanka
22 апреля 2017 в 21:07
1

Геттеры и сеттеры, поступающие из , скрывающие данные . Скрытие данных означает, что мы скрывают данные от посторонних или посторонний человек / объект не может получить доступ наши данные. Это полезная функция в ООП.

Например:

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

public и private - модификаторы доступа.

Итак, как мы можем получить доступ к этой переменной извне:

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

Пример (Java):

private String name;

public String getName(){
   return this.name;
}

public void setName(String name){
   this.name= name;
}

Преимущество :

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

//assume we have person1 object
//to give permission to check balance
person1.getName()

//to give permission to set balance
person1.setName()

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

Bill K
1 мая 2017 в 16:57
0

ваш getBalance / setBalance не является геттером / сеттером, если у них есть другой код, не так ли? И зачем выставлять баланс? Разве не было бы безопаснее иметь applyPayment или applyDebt, позволяющую проверять баланс, и, возможно, поле для заметок, чтобы указать, откуда был произведен платеж? Привет! Я просто улучшил ваш дизайн И удалил сеттер и получатель. В этом особенность сеттеров / получателей: они почти всегда улучшают ваш код, удаляя их, не то чтобы они «неправильные», просто они почти всегда приводят к худшему коду. Кстати, свойства (как в C #) имеют точно такую ​​же проблему.

Heetola
6 мая 2020 в 07:43
1

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

avatar
Rakesh Singh
3 января 2017 в 16:10
2

Одно относительно современное преимущество геттеров / сеттеров состоит в том, что они упрощают просмотр кода в редакторах тегированного (индексированного) кода. Например. Если вы хотите увидеть, кто устанавливает член, вы можете открыть иерархию вызовов установщика.

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

Heetola
6 мая 2020 в 07:42
0

вы можете щелкнуть правой кнопкой мыши> найти использование на члене точно так же, как на геттере / сеттере

avatar
kangalioo
30 августа 2016 в 18:59
23

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

Я провел небольшой тест производительности. Я написал класс NumberHolder, который содержит целое число. Вы можете прочитать это целое число с помощью метода получения anInstance.getNumber() или путем прямого доступа к номеру с помощью anInstance.number. Моя программа считывает число 1 000 000 000 раз в обоих направлениях. Этот процесс повторяется пять раз, и время печатается. Получился следующий результат:

Time 1: 953ms, Time 2: 741ms
Time 1: 655ms, Time 2: 743ms
Time 1: 656ms, Time 2: 634ms
Time 1: 637ms, Time 2: 629ms
Time 1: 633ms, Time 2: 625ms

(Время 1 - прямой путь, Время 2 - получатель)

Видите ли, геттер (почти) всегда немного быстрее. Потом попробовал с разным количеством циклов. Вместо 1 миллиона я использовал 10 миллионов и 0,1 миллиона. Результаты:

10 миллионов циклов:

Time 1: 6382ms, Time 2: 6351ms
Time 1: 6363ms, Time 2: 6351ms
Time 1: 6350ms, Time 2: 6363ms
Time 1: 6353ms, Time 2: 6357ms
Time 1: 6348ms, Time 2: 6354ms

При 10 миллионах циклов время почти такое же. Вот 100 тысяч (0,1 миллиона) циклов:

Time 1: 77ms, Time 2: 73ms
Time 1: 94ms, Time 2: 65ms
Time 1: 67ms, Time 2: 63ms
Time 1: 65ms, Time 2: 65ms
Time 1: 66ms, Time 2: 63ms

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

Alex
6 сентября 2016 в 17:28
4

Есть «заметные» накладные расходы, связанные с вызовом функции для доступа к памяти вместо простой загрузки адреса объекта и добавления смещения для доступа к членам. Скорее всего, виртуальная машина все равно оптимизировала ваш геттер. Тем не менее, упомянутые накладные расходы не стоят того, чтобы терять все преимущества геттеров / сеттеров.

avatar
Jean-Christophe Blanchard
4 февраля 2016 в 22:40
0

Хотя эти методы не являются обычным явлением для геттеров и сеттеров, их также можно использовать в шаблонах AOP / прокси. например, для аудита переменной вы можете использовать АОП для аудита обновления любого значения. Без геттера / сеттера это невозможно, кроме как повсюду менять код. Лично я никогда не использовал для этого АОП, но это показывает еще одно преимущество использования геттера / сеттера.

avatar
Pritam Banerjee
10 декабря 2015 в 20:44
4

Геттеры и сеттеры используются для реализации двух фундаментальных аспектов объектно-ориентированного программирования, а именно:

  1. Абстракция
  2. Инкапсуляция

Предположим, у нас есть класс Employee:

package com.highmark.productConfig.types;

public class Employee {

    private String firstName;
    private String middleName;
    private String lastName;

    public String getFirstName() {
      return firstName;
    }
    public void setFirstName(String firstName) {
       this.firstName = firstName;
    }
    public String getMiddleName() {
        return middleName;
    }
    public void setMiddleName(String middleName) {
         this.middleName = middleName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getFullName(){
        return this.getFirstName() + this.getMiddleName() +  this.getLastName();
    }
 }

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

FacelessTiger
14 декабря 2016 в 01:06
3

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

Andrew Hows
8 декабря 2017 в 00:42
2

Преимущество этого состоит в том, что вы можете изменить внутреннюю структуру класса, не меняя интерфейс. Скажем, вместо трех свойств у вас было одно - массив строк. Если вы использовали геттеры и сеттеры, вы можете внести это изменение, а затем обновить геттеры / сеттеры, чтобы знать, что имена [0] - это имя, имена [1] - среднее и т. Д. Но если вы просто использовали общедоступные свойства , вам также придется изменить каждый класс, к которому осуществляется доступ Employee, потому что свойство firstName, которое они использовали, больше не существует.

Heetola
6 мая 2020 в 07:41
3

@AndrewHows из того, что я видел в реальной жизни, когда люди меняют внутреннюю часть класса, они также меняют интерфейс и проводят большой рефакторинг всего кода

avatar
Haakon Løtveit
29 ноября 2015 в 14:37
20

РЕДАКТИРОВАТЬ: Я ответил на этот вопрос, потому что множество людей, изучающих программирование, задают этот вопрос, и большинство ответов очень технически компетентны, но их не так легко понять, если вы новичок. Мы все были новичками, поэтому я решил попробовать свои силы в более дружественном для новичков ответе.

Два основных из них - полиморфизм и проверка. Даже если это просто дурацкая структура данных.

Допустим, у нас есть этот простой класс:

public class Bottle {
  public int amountOfWaterMl;
  public int capacityMl;
}

Очень простой класс, показывающий, сколько жидкости в нем и какова его емкость (в миллилитрах).

Что происходит, когда я делаю:

Bottle bot = new Bottle();
bot.amountOfWaterMl = 1500;
bot.capacityMl = 1000;

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

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

В итоге мы получим что-то вроде:

public interface LiquidContainer {
  public int getAmountMl();
  public void setAmountMl(int amountMl);
  public int getCapacityMl();
}

Отлично! А теперь просто изменим Bottle на это:

public class Bottle extends LiquidContainer {
  private int capacityMl;
  private int amountFilledMl;

  public Bottle(int capacityMl, int amountFilledMl) {
    this.capacityMl = capacityMl;
    this.amountFilledMl = amountFilledMl;
    checkNotOverFlow();
  }

  public int getAmountMl() {
    return amountFilledMl;
  }

  public void setAmountMl(int amountMl) {
     this.amountFilled = amountMl;
     checkNotOverFlow();
  }
  public int getCapacityMl() {
    return capacityMl;
  }

  private void checkNotOverFlow() {
    if(amountOfWaterMl > capacityMl) {
      throw new BottleOverflowException();
    }
}

Я оставлю определение исключения BottleOverflowException в качестве упражнения для читателя.

Теперь обратите внимание, насколько это надежнее. Теперь мы можем работать с любым типом контейнера в нашем коде, приняв LiquidContainer вместо Bottle. И то, как эти бутылки справляются с подобными вещами, может быть разным. У вас могут быть бутылки, которые записывают свое состояние на диск при его изменении, или бутылки, которые сохраняются в базах данных SQL или GNU знает что еще.

И все они могут иметь разные способы справиться с различными воплями. Бутылка просто проверяет и, если она переполняется, выдает исключение RuntimeException. Но это может быть неправильным поступком. (Есть полезное обсуждение обработки ошибок, но я намеренно сохраняю его очень простым. Люди в комментариях, скорее всего, укажут на недостатки этого упрощенного подхода.;))

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

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

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

djvs
10 февраля 2016 в 07:41
2

Спасибо за первое приличное объяснение интерфейсов, которое я прочитал, без каких-либо ссылок на «дистанционное управление» или «автомобили».

Xerus
18 октября 2017 в 20:15
3

«У вас могут быть бутылки, которые записывают свое состояние на диск при его изменении, или бутылки, которые сохраняются в базах данных SQL», - так сильно я набросился на xD. Но в любом случае, хорошая идея подарить это!

avatar
Bartłomiej Sobieszek
22 августа 2015 в 00:58
-2

Это хороший вопрос, и ответы на него еще лучше. Так как их много, просто добавлю еще Этот пример основан на C #, просто полезный фрагмент кода. Подтверждение данных в скобках уже объяснялось.

public class foo
{
    public int f1 { get; set; }             // A classic GS
    public int f2 { get; private set; }     // A GS with public read access, the write is only on the private level
    public int f3 { private get; set; }     // A GS where "You can set, but you can't get" outside the class
    public int f4 { get; set; } = 10;       // A GS with default value, this is a NEW feature of C# 6.0 / .NET 4.6
}
Haakon Løtveit
16 декабря 2015 в 13:37
0

В Java таких геттеров и сеттеров нет. У вас есть Project Lombok, в котором аннотации используются почти так же. Так что, если вам нужна эта изящность, но на Java, а не на C #, тогда вы знаете, куда идти. :)

avatar
yegor256
23 сентября 2014 в 16:39
62

В чисто объектно-ориентированном мире геттеры и сеттеры - это ужасный антипаттерн . Прочтите эту статью: Геттеры / сеттеры. Зло. Период. Короче говоря, они побуждают программистов думать об объектах как о структурах данных, и этот тип мышления является чисто процедурным (как в COBOL или C). В объектно-ориентированном языке нет структур данных, а есть только объекты, которые демонстрируют поведение (не атрибуты / свойства!)

Вы можете найти больше о них в разделе 3.5 документа Elegant Objects (моя книга об объектно-ориентированном программировании).

Raedwald
12 декабря 2014 в 08:31
7

Геттеры и сеттеры предлагают анемичную модель предметной области.

Stephen C
1 июля 2016 в 07:29
10

Интересная точка зрения. Но в большинстве случаев программирования нам нужны структуры данных. Возьмем пример "Собака" из связанной статьи. Да, вы не можете изменить вес реальной собаки, установив атрибут ... но new Dog() не собака. Это объект, содержащий информацию о собаке. И для этого использования естественно иметь возможность исправить неправильно записанный вес.

Stephen C
2 июля 2016 в 00:34
4

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

Bill K
1 мая 2017 в 16:51
3

Реальный мир или нет, Егор совершенно прав. Если у вас действительно есть «структура» и вам не нужно писать какой-либо код, который ссылается на нее по имени, поместите ее в хеш-таблицу или другую структуру данных. Если вам действительно нужно написать для него код, поместите его как член класса и поместите код, который управляет этой переменной, в тот же класс и опустите установщик и получатель. PS. хотя я в основном разделяю точку зрения Егора, я пришел к выводу, что аннотированные bean-компоненты без кода являются в некоторой степени полезными структурами данных - также иногда необходимы геттеры, а сеттеры никогда не должны существовать.

Bill K
1 мая 2017 в 17:03
1

Я увлекся - весь этот ответ, хотя и правильный и актуальный, не затрагивает вопрос напрямую. Возможно, следует сказать: «И сеттеры / геттеры, и общедоступные переменные неверны» ... Чтобы быть абсолютно конкретным, сеттеры и доступные для записи общедоступные переменные никогда не должны использоваться, тогда как геттеры в значительной степени идентичны общедоступным конечным переменным и иногда являются необходимым злом. но ни один из них не намного лучше другого.

C Perkins
14 мая 2017 в 01:09
0

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

meguli
5 июня 2017 в 14:24
1

Я никогда не понимал геттеров / сеттеров. С первого курса Java101, который я прошел в колледже, казалось, что они поощряют строительство без абстракций, и сбили меня с толку. Но есть такой случай: ваш объект может иметь очень много разных поведений. Тогда возможно, что вы не надеетесь реализовать их все. Вы создаете сеттеры / получатели и удобно сообщаете своим пользователям, что если вам нужно поведение, которое я пропустил, наследуйте и реализуйте их самостоятельно. В конце концов, я дал вам аксессуары.

Matthias Ronge
14 мая 2018 в 11:41
0

В этом смысле «живые объекты» не должно быть set…() и get…(), а скорее remember…() и tell…(). Просто то, что get и set короче для ввода. Кроме того, объект, описывающий другой объект - например, форма в бумажном переплете - должен включать это в имя класса, но это опять же длинно. Мы не можем изменить вес собаки, и мы не делаем этого, мы меняем только вес, указанный в форме приема собаки.

avatar
abarnert
19 августа 2014 в 04:47
5

Вы должны использовать геттеры и сеттеры, когда:

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

Так что это очень редко общий вопрос OO; это вопрос, относящийся к конкретному языку, с разными ответами для разных языков (и разными вариантами использования).


С точки зрения объектно-ориентированной теории геттеры и сеттеры бесполезны. Интерфейс вашего класса - это то, что он делает, а не его состояние. (Если нет, значит, вы написали неправильный класс.) В очень простых случаях, когда класс делает просто, например представляют точку в прямоугольных координатах, * атрибуты являются частью интерфейса; геттеры и сеттеры просто затуманивают это. Но во всех случаях, кроме очень простых, ни атрибуты, ни геттеры и сеттеры не являются частью интерфейса.

Иными словами: если вы считаете, что потребители вашего класса не должны даже знать, что у вас есть атрибут spam, а тем более иметь возможность изменить его волей-неволей, то предоставление им метода set_spam - это то, что вам нужно. последнее, что ты хочешь сделать.

* Даже для этого простого класса вам не обязательно разрешать установку значений x и y. Если это действительно класс, разве у него не должно быть таких методов, как translate, rotate и т. Д.? Если это всего лишь класс, потому что в вашем языке нет записей / структур / именованных кортежей, тогда это действительно не вопрос OO…


Но никто никогда не занимается общим объектно-ориентированным дизайном. Они занимаются дизайном и реализацией на определенном языке. А в некоторых языках геттеры и сеттеры далеко не бесполезны.

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

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

Что касается вопроса «что, если я захочу изменить свою реализацию позже?» вопрос (который повторяется несколько раз в разных формулировках как в вопросе OP, так и в принятом ответе): если это действительно чистое изменение реализации, и вы начали с атрибута, вы можете изменить его на свойство, не затрагивая интерфейс. Если, конечно, ваш язык этого не поддерживает. Так что это действительно тот же случай.

Также важно следовать идиомам языка (или фреймворка), который вы используете. Если вы напишете красивый код в стиле Ruby на C #, у любого опытного разработчика C #, кроме вас, будут проблемы с его чтением, а это плохо. Некоторые языки имеют более сильную культуру вокруг своих соглашений, чем другие. - и, возможно, не случайно, что Java и Python, которые находятся на противоположных концах спектра идиоматических геттеров, имеют две из самых сильных культур.

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

avatar
GeZo
13 мая 2014 в 09:47
3

Есть веская причина рассмотреть возможность использования аксессоров, если нет наследования свойств. См. Следующий пример:

public class TestPropertyOverride {
    public static class A {
        public int i = 0;

        public void add() {
            i++;
        }

        public int getI() {
            return i;
        }
    }

    public static class B extends A {
        public int i = 2;

        @Override
        public void add() {
            i = i + 2;
        }

        @Override
        public int getI() {
            return i;
        }
    }

    public static void main(String[] args) {
        A a = new B();
        System.out.println(a.i);
        a.add();
        System.out.println(a.i);
        System.out.println(a.getI());
    }
}

Вывод:

0
0
4
avatar
jfritz42
6 мая 2014 в 18:19
0

Одно относительно современное преимущество геттеров / сеттеров состоит в том, что они упрощают просмотр кода в редакторах тегированного (индексированного) кода. Например. Если вы хотите увидеть, кто устанавливает член, вы можете открыть иерархию вызовов установщика.

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

avatar
Jonathan
1 апреля 2014 в 00:46
0

Если вам нужна переменная только для чтения, но вы не хотите, чтобы клиент менял способ доступа к ней, попробуйте этот шаблонный класс:

template<typename MemberOfWhichClass, typename primative>                                       
class ReadOnly {
    friend MemberOfWhichClass;
public:
    template<typename number> inline bool   operator==(const number& y) const { return x == y; } 
    template<typename number> inline number operator+ (const number& y) const { return x + y; } 
    template<typename number> inline number operator- (const number& y) const { return x - y; } 
    template<typename number> inline number operator* (const number& y) const { return x * y; }  
    template<typename number> inline number operator/ (const number& y) const { return x / y; } 
    template<typename number> inline number operator<<(const number& y) const { return x << y; }
    template<typename number> inline number operator^(const number& y) const  { return x^y; }
    template<typename number> inline number operator~() const                 { return ~x; }
    template<typename number> inline operator number() const                  { return x; }
protected:
    template<typename number> inline number operator= (const number& y) { return x = y; }       
    template<typename number> inline number operator+=(const number& y) { return x += y; }      
    template<typename number> inline number operator-=(const number& y) { return x -= y; }      
    template<typename number> inline number operator*=(const number& y) { return x *= y; }      
    template<typename number> inline number operator/=(const number& y) { return x /= y; }      
    primative x;                                                                                
};      

Пример использования:

class Foo {
public:
    ReadOnly<Foo, int> cantChangeMe;
};

Помните, что вам также нужно будет добавить побитовые и унарные операторы! Это просто для начала

Hubert Grzeskowiak
1 августа 2016 в 09:59
0

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

avatar
MongooseLover
15 декабря 2013 в 20:31
1

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

Например, переменная, которую вы никогда не намеревались использовать вне класса, может быть доступна даже напрямую через доступ к цепочке переменных (например, object.item.origin.x).

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

Agi Hammerthief
18 декабря 2014 в 20:37
0

Ярким примером этого является случай, когда установка значения одного атрибута влияет на одно или несколько других значений атрибута. Тривиальный пример: предположим, что вы сохраняете радиус круга. Использование circ.set_radius () `на самом деле ничего не дает, установка радиуса напрямую через circ.radius не дает. Однако get_diameter (), get_circumference () и get_area () могут выполнять вычисления на основе радиуса. Без геттеров вы должны сами выполнить вычисления и проверить их правильность.

avatar
luke1985
26 ноября 2013 в 11:52
-1

Пусть код говорит сам за себя:

Mesh mesh = new Mesh();
BoundingVolume vol = new BoundingVolume();
mesh.boundingVolume = vol;
vol.mesh = mesh;
vol.compute(); 

Тебе это нравится? Вот с установщиками:

Mesh mesh = new Mesh();
BoundingVolume vol = new BoundingVolume();
mesh.setBoundingVolume(vol);
AgilePro
28 октября 2014 в 00:33
5

Вы не ответили на вопрос: "В чем преимущество использования геттеров и сеттеров - , которые только получают и устанавливают ..." Ваш пример не относится к этому случаю, и поэтому ваш ответ не относится к этому вопрос.

Hubert Grzeskowiak
1 августа 2016 в 09:58
0

Я считаю, что семантика приведенных примеров кода совершенно другая. В первом примере также создается круговая ссылка: mesh.boundingVolume.mesh == mesh

avatar
Devrath
2 сентября 2013 в 06:51
17

Мы используем геттеры и сеттеры:

  • для повторного использования
  • для выполнения проверки на более поздних этапах программирования

Методы получения и установки являются общедоступными интерфейсами для доступа к частным членам класса.


Мантра инкапсуляции

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

Методы получения: Мы можем получить доступ к частным переменным.

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

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

  • лучше;
  • безопаснее; и
  • быстрее.

Везде, где можно использовать значение, можно добавить метод, возвращающий это значение. Вместо:

int x = 1000 - 500

использовать

int x = 1000 - class_name.getValue();

Говоря простым языком

Representation of "Person" class

Предположим, нам нужно сохранить детали этого Person. Этот Person имеет поля name, age и sex. Для этого нужно создать методы для name, age и sex. Теперь, если нам нужно создать другого человека, необходимо снова создать методы для name, age, sex.

Вместо этого мы можем создать bean-компонент class(Person) с помощью методов получения и установки. Итак, завтра мы можем просто создавать объекты этого Bean class(Person class) всякий раз, когда нам нужно добавить нового человека (см. Рисунок). Таким образом, мы повторно используем поля и методы класса bean-компонента, что намного лучше.

avatar
bobobobo
5 мая 2013 в 02:07
4

Код развивается . private отлично подходит, когда вам нужна защита элементов данных . В конце концов, все классы должны быть своего рода «минипрограммами» с четко определенным интерфейсом , который нельзя просто запутать внутренностями .

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

int getVar() const { return var ; }

Итак, у вас есть:

doSomething( obj->getVar() ) ;

Вместо

doSomething( obj->var ) ;

getVar() не только визуально шумит, но и создает иллюзию того, что gettingVar() каким-то образом является более сложным процессом, чем он есть на самом деле. То, как вы (как автор класса) относитесь к святости var, особенно сбивает с толку пользователя вашего класса, если у него есть установщик passthru - тогда похоже, что вы устанавливаете эти ворота, чтобы «защитить» то, на чем вы настаиваете. ценно (святость var), но даже вы признаете, что защита var не стоит многого из-за возможности любого просто войти и set var с любой ценностью, которую они хотят, без ты даже заглядывал в то, что они делают.

Итак, я программирую следующим образом (предполагая "гибкий" подход - то есть когда я пишу код, не зная точно , что он будет делать / у меня нет времени или опыта, чтобы спланировать сложный водопад набор интерфейсов стиля):

1) Начните со всех открытых членов для основных объектов с данными и поведением. Вот почему во всем моем "примере" кода C ++ вы заметите, что я везде использую struct вместо class.

2) Когда внутреннее поведение объекта для члена данных становится достаточно сложным (например, ему нравится сохранять внутренний std::list в некотором порядке), записываются функции типа доступа. Поскольку я программирую сам, я не всегда сразу устанавливаю член private, но где-то в процессе развития класса член будет «повышен» до protected или private.

3) Классы, которые полностью конкретизированы и имеют строгие правила в отношении их внутреннего устройства (т.е. они точно знают, что они делают, и вы не должны «трахать» (технический термин) его внутреннее устройство) дается обозначение class, частные члены по умолчанию, и только несколько избранных членов могут быть public.

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

Agi Hammerthief
18 декабря 2014 в 20:45
1

«... четко определенный интерфейс, с которым нельзя просто прикрутить внутренности» и проверка в установщиках.

avatar
Sumit Singh
30 августа 2012 в 13:02
3

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

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

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

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

avatar
R. Martinho Fernandes
24 августа 2012 в 10:55
392

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

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

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

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

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

Если у класса есть эти глупые геттеры и сеттеры для каждого поля, то это класс, который не имеет никаких инвариантов, без контракта . Это действительно объектно-ориентированный дизайн? Если все, что у класса есть, - это геттеры и сеттеры, это просто тупой держатель данных, а тупые держатели данных должны выглядеть как тупые держатели данных:

class Foo {
public:
    int DaysLeft;
    int ContestantNumber;
};

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

Клиент : «Что я могу сделать с объектом этого класса?»
Дизайнер : «Вы можете читать и записывать несколько переменных» <2938890338846352> > Клиент : «О ... круто, я думаю?»

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

Lightness Races in Orbit
6 июля 2013 в 21:12
17

Отличный ответ (+1). Моя единственная критика заключается в том, что мне потребовалось несколько чтений, чтобы понять, чем «проверка» в последнем абзаце отличается от «проверки» в первых нескольких (которую вы отбросили во втором случае, но продвинули в первом); изменение формулировки может помочь в этом отношении.

Darrell Teague
6 ноября 2013 в 14:58
14

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

Raedwald
12 декабря 2014 в 08:38
2

Ключевое наблюдение заключается в том, что геттеры гораздо полезнее, чем сеттеры. Получатель может возвращать вычисленное значение или кэшированное вычисленное значение. Все, что может сделать установщик, - это некоторая проверка, а затем изменение поля private. Если у класса нет сеттеров, его легко сделать неизменяемым.

Toskan
4 августа 2015 в 11:30
0

Я согласен с большинством пунктов, особенно с YAGNI. НО: я использовал программу, которая создавала структуру объекта из нетривиального другого объекта. Произошла ошибка, один участник был обновлен неправильно. Я искал вхождения: 380 вхождений. Из этих 380 вхождений только 40 устанавливали значение, остальные извлекали его. И только 1 сеттер вызвал ошибку. Если бы этот парень использовал сеттеры, я бы сэкономил много времени, потому что мне не пришлось бы смотреть на 340 строк кода, если бы он только извлекал значение.

R. Martinho Fernandes
4 августа 2015 в 14:33
1

@Toskan - это просто пример случая, когда в вашем классе есть инварианты, а дизайнер забыл о них. Это не пример против моего случая, это «за».

Toskan
5 августа 2015 в 00:05
0

@ R.MartinhoFernandes ehrm, вы можете указать, о чем вы имеете в виду? Поскольку нет, это нетривиальная структура, инварианты не являются решением.

R. Martinho Fernandes
5 августа 2015 в 08:25
7

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

user1914692
9 февраля 2016 в 19:48
0

@PreferenceBean Итак, что означает «проверка» в последнем абзаце? Я сбит с толку, как и вы.

user1914692
9 февраля 2016 в 19:49
0

Мне нравится аргумент. Да, договор означает, что пользователь не ожидал никаких изменений.

alexis
8 июня 2016 в 00:02
2

@user, "проверка" означает одно и то же в обоих местах. Разница (как объясняется в последнем абзаце) заключается в том, что аксессоры оправданы, если вы реализуете проверку с самого начала , а не «на всякий случай, когда я хочу добавить проверку позже».

Joeri Hendrickx
29 августа 2016 в 06:53
5

+1 за «тупые держатели данных должны выглядеть как тупые держатели данных». К сожалению, в наши дни все, кажется, проектируют все вокруг тупых держателей данных, и многие фреймворки даже требуют этого ...

Şafak Gür
21 марта 2018 в 09:12
1

...this claim that you can change the implementation and your clients don't have to be recompiled. ложно только при изменении контракта. Вы можете изменить все внутренние компоненты типа, не изменяя его контракт, и именно такая возможность является причиной того, почему инкапсуляция - это вещь. Вы правы, утверждая, что изменение контракта без просмотра кода, который зависит от него, является злом, но не потому, что аксессоры / свойства существуют в любом случае - это скорее неправильное использование соглашения, которое почти всегда является лучшей практикой, если только тип не гарантированно имеет те же внутренности / реализация.

Yuval Perelman
24 декабря 2018 в 13:19
3

+1 за отличный ответ, но есть одно и, как ни странно, только одно преимущество геттеров и сеттеров в «тупых держателях данных»: большинство IDE скомпилированных языков имеют встроенную возможность «найти все ссылки» на метод или свойство. Возможность найти все места, где данные устанавливаются с отфильтрованным геттером, за эти годы сэкономила мне огромное количество времени, и мне трудно представить отладку сложного кода без этого.

jrh
17 августа 2020 в 15:25
0

@Toskan, имеющий сеттер / получатель, не полностью решает проблему «откуда я знаю, что написал в него», у меня был проект, в котором использовались геттеры / сеттеры, но сам объект не использовал свой сеттер при изменении своего собственного состояния , что он делал часто. Поэтому всякий раз, когда я хотел узнать, изменилось ли поле, мне приходилось смотреть на все случаи использования его установщика, а также на все случаи, когда объект записывал в свое собственное внутреннее поле. Вы можете сказать, что можете исправить это, если сам объект всегда будет использовать свой собственный сеттер, но это может вызвать бесконечные рекурсии и ненужные срабатывания событий.

jrh
17 августа 2020 в 15:29
0

... или, другими словами, get / setters могут иногда добавлять контекст «кто хочет знать» / «кто это установил», что, на мой взгляд, обычно является более сложным, чем оно того стоит. Обычно приводит к зависанию пользовательских интерфейсов, которые слишком часто перерисовываются, и к неэффективным сетевым соединениям, которые отправляют 100 МБ данных по 32 бита за раз. Я сильно упрощаю здесь, но я никогда не замечал, что говорю «ну, если бы только этот объект мог обновлять другие вещи», и я довольно часто говорил: «Мне нужно сделать этот хак, чтобы предотвратить это массовое обновление, вызывающее 1000 событий». .

Toskan
17 августа 2020 в 18:29
0

@jrh моя IDE в настоящее время может обнаруживать и различать чтение / запись в поле. 5 лет назад этого не было

jrh
17 августа 2020 в 19:26
0

@Toskan что это за IDE?

Toskan
17 августа 2020 в 23:19
1

@jrh Я не использовал java в течение многих лет, но для некоторых языков, таких как javascript и php intellij (с phpstorm и webstorm, интегрированными afaik), помогает.

jrh
17 августа 2020 в 23:30
0

@Toskan - это довольно интересная функция, я никогда не видел ее ни в одной из IDE, которые я использовал (кроме VS с C ++, и когда я попробовал ее, ее реализация была глубоко ошибочной и неточной). Надеюсь, это будет реализовано в большем количестве редакторов.

avatar
Jorge Aguilar
22 августа 2012 в 14:47
26

Ну, я просто хочу добавить, что даже если иногда они необходимы для инкапсуляции и безопасности ваших переменных / объектов, если мы хотим закодировать настоящую объектно-ориентированную программу, тогда нам нужно ОСТАНОВИТЬ ПЕРЕСЕЧЕНИЕ АКСЕССУАРЫ , потому что иногда мы сильно зависим от них, когда в этом нет необходимости, и это почти то же самое, как если бы мы сделали переменные общедоступными.

avatar
Mohamed
14 июля 2012 в 16:20
16

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

Думайте просто, легко, при необходимости добавляйте сложности.

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

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

avatar
andrers52
30 апреля 2012 в 18:57
4

С точки зрения объектно-ориентированного дизайна обе альтернативы могут нанести ущерб сопровождению кода из-за ослабления инкапсуляции классов. Для обсуждения вы можете заглянуть в эту отличную статью: http://typicalprogrammer.com/?p=23

avatar
user595447
27 марта 2012 в 03:53
27

Спасибо, это действительно прояснило мои мысли. Вот (почти) 10 (почти) веских причин НЕ использовать геттеры и сеттеры:

  1. Когда вы понимаете, что вам нужно сделать больше, чем просто установить и получить значение, вы можете просто сделать поле частным, что сразу же сообщит вам, где вы непосредственно к нему обратились.
  2. Любая проверка, которую вы выполняете там, может быть только контекстно-свободной, что на практике встречается редко.
  3. Вы можете изменить установленное значение - это абсолютный кошмар, когда вызывающий передает вам значение, которое они [ужас шока] хотят, чтобы вы сохранили КАК ЕСТЬ.
  4. Вы можете скрыть внутреннее представление - фантастика, значит, вы убедитесь, что все эти операции симметричны, верно?
  5. Вы изолировали свой общедоступный интерфейс от изменений под листами - если вы разрабатывали интерфейс и не были уверены, что прямой доступ к чему-либо в порядке, то вам следовало продолжить проектирование.
  6. Некоторые библиотеки ожидают этого, но не многие - отражение, сериализация, фиктивные объекты прекрасно работают с общедоступными полями.
  7. Унаследовав этот класс, вы можете переопределить функциональность по умолчанию - другими словами, вы можете ДЕЙСТВИТЕЛЬНО запутать вызывающих абонентов, не только скрывая реализацию, но и делая ее несовместимой.

Последние три я просто ухожу (N / A или D / C) ...

sbi
24 августа 2012 в 09:29
12

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

avatar
ebt
8 июля 2010 в 22:17
0

Я хотел опубликовать реальный пример, который я только что закончил:

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

package com.foo.entities.custom
class User extends com.foo.entities.User{
     public Integer getSomething(){
         return super.getSomething();             
     }
     public void setSomething(Integer something){
         something+=1;
         super.setSomething(something); 
     }
}

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

R. Martinho Fernandes
24 августа 2012 в 14:39
4

«То, что я сделал выше, - это переопределить существующие методы суперкласса моей новой функциональностью». С функциональностью, которая, как мы все надеемся, уже должным образом задокументирована для старого интерфейса, верно? В противном случае вы просто нарушите LSP и внесли незаметное критическое изменение, которое было бы обнаружено компилятором, если бы в поле зрения не было геттеров / сеттеров.

Phil
29 октября 2013 в 19:15
0

@ R.MartinhoFernandes "... а затем запустить инструменты гибернации для генерации java-кода" это не случай изменения поведения какого-то класса, это работа над дерьмовым инструментом. Возможно, контракт (который в данном случае находится в подклассе) всегда был для этого «сюрприза». Может быть аргументом в пользу has-a вместо is-a.

Agi Hammerthief
18 декабря 2014 в 20:59
0

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

avatar
Thorbjørn Ravn Andersen
7 ноября 2009 в 21:55
16

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

  1. Код интерфейса, а не реализация
  2. Интерфейсы определяют только методы, а не поля

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

Эти методы - печально известные методы получения и установки ....

Dean J
9 ноября 2009 в 15:22
2

Хорошо, второй вопрос; в случае, если это проект, в котором вы никому не экспортируете исходный код, и у вас есть полный контроль над источником ... получаете ли вы что-нибудь с помощью геттеров и сеттеров?

Thorbjørn Ravn Andersen
9 ноября 2009 в 18:13
2

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

avatar
fastcodejava
14 октября 2009 в 22:40
1

Я просто хотел бы подбросить идею аннотации: @getter и @setter. С @getter вы должны иметь возможность obj = class.field, но не class.field = obj. С @setter наоборот. С @getter и @setter вы сможете делать и то, и другое. Это сохранит инкапсуляцию и сократит время, не вызывая тривиальные методы во время выполнения.

Phil
29 октября 2013 в 19:11
1

Это будет реализовано во время выполнения с помощью «тривиальных методов». На самом деле, наверное, нетривиально.

Thorbjørn Ravn Andersen
7 января 2018 в 19:56
0

Сегодня это можно сделать с помощью препроцессора аннотаций.

avatar
Kai
14 октября 2009 в 21:00
101

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

Допустим, вы просматриваете сотни строк кода и наталкиваетесь на следующее:

person.name = "Joe";

Это красиво простой фрагмент кода, пока вы не поймете, что это сеттер. Теперь вы следуете за этим установщиком и обнаруживаете, что он также устанавливает person.firstName, person.lastName, person.isHuman, person.hasReallyCommonFirstName и вызывает person.update (), который отправляет запрос в базу данных и т. Д. где произошла утечка памяти.

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

Fadecomic
14 мая 2012 в 19:56
9

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

Phil
29 октября 2013 в 19:02
38

Это аргумент против синтаксического сахара, а не против сеттеров в целом.

Darrell Teague
6 ноября 2013 в 15:26
6

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

Honza Zidek
13 апреля 2017 в 10:42
0

«Это красиво простой фрагмент кода, пока вы не поймете, что это сеттер» - эй, это был исходный пост о Java или о C #? :)

Will
18 октября 2017 в 10:41
0

По поводу удобочитаемости полностью согласен. Совершенно очевидно, что происходит при вызове person.name = "Joe";. В информации нет беспорядка, выделение синтаксиса показывает, что это поле, а рефакторинг проще (нет необходимости реорганизовывать два метода и поле). Во-вторых, можно забыть все эти потраченные впустую мозговые циклы с мыслями о «getIsValid ()» или «isValid ()» и т. Д. Я не могу вспомнить ни одного случая, когда меня спасли от ошибки геттер / сеттер.

Armando SM
18 мая 2020 в 09:10
0

что это больше проблема с очисткой кода, чем с геттерами и сеттерами, я думаю

OCDev
9 июля 2020 в 02:36
0

@Phil Вы написали: «Это аргумент против синтаксического сахара, а не против сеттеров в целом». Подожди минуту. Если сеттеры - это синтаксический сахар, как вы подразумеваете, то, будучи аргументом против синтаксического сахара, они также являются аргументом против сеттеров.

Phil
10 июля 2020 в 06:28
0

@OCDev совсем наоборот. Когда геттеры и сеттеры замаскированы под доступ к свойствам (синтаксический шугаринг), они скрывают тот факт, что может происходить нечто большее, чем простое присваивание. Могут быть аргументы против геттеров и сеттеров, делающих больше, чем описано на банке, но если они действительно делают больше, чем описано на банке, лучше не засахаривать, потому что это еще больше маскирует то, что происходит. на.

mireazma
8 октября 2020 в 17:32
0

У меня проблемы с отладкой такого простого свойства, как просмотр его значения. Кроме того, свойства - это инструмент, который дает пользователям возможность втиснуть невероятные объемы кода в ... казалось бы, невинную переменную. И я сторонник «свободы» в языке.

avatar
Antz
14 октября 2009 в 20:01
2

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

avatar
quillbreaker
14 октября 2009 в 19:08
15

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

У меня был класс базовой страницы в переданном мне проекте, который загружал некоторые данные из пары различных вызовов веб-служб, но данные в этих вызовах веб-служб не всегда использовались на всех дочерних страницах. Web-сервисы, несмотря на все преимущества, являются первопроходцами в новых определениях термина «медленный», поэтому вы не хотите делать вызовы веб-сервисов, если вам не нужно.

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

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

    protected YourType _yourName = null;
    public YourType YourName{
      get
      {
        if (_yourName == null)
        {
          _yourName = new YourType();
          return _yourName;
        }
      }
    }
icc97
17 сентября 2014 в 20:24
0

Итак, геттер вызывает сеттер?

quillbreaker
19 сентября 2014 в 18:20
0

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

Sara M
20 декабря 2017 в 20:05
0

Это C # docs.microsoft.com/en-us/dotnet/csharp/programming-guide/…

avatar
jdehaan
14 октября 2009 в 18:38
13

Один аспект, который я пока упустил в ответах, это спецификация доступа:

  • для членов у вас есть только одна спецификация доступа для установки и получения
  • для сеттеров и геттеров вы можете точно настроить и определить его отдельно
avatar
jcdyer
14 октября 2009 в 18:32
29

Зависит от вашего языка. Вы отметили это «объектно-ориентированным», а не «Java», поэтому я хотел бы отметить, что ответ ChssPly76 зависит от языка. В Python, например, нет причин использовать геттеры и сеттеры. Если вам нужно изменить поведение, вы можете использовать свойство, которое обертывает геттер и сеттер вокруг доступа к базовому атрибуту. Примерно так:

 class Simple(object):
   def _get_value(self):
       return self._value -1

   def _set_value(self, new_value):
       self._value = new_value + 1

   def _del_value(self):
       self.old_values.append(self._value)
       del self._value

   value = property(_get_value, _set_value, _del_value)
ChssPly76
14 октября 2009 в 18:40
2

Да, я уже сказал об этом в комментарии под моим ответом. Java - не единственный язык, использующий геттеры / сеттеры в качестве опоры, точно так же, как Python - не единственный язык, способный определять свойства. Но главное остается неизменным - «собственность» - это не то же самое «публичное поле».

jcdyer
15 октября 2009 в 13:35
0

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

ChssPly76
15 октября 2009 в 21:09
1

@jcd - совсем нет. Вы определяете свой «интерфейс» (публичный API был бы здесь более подходящим термином), открывая свои публичные поля. Как только это будет сделано, пути назад нет. Свойства НЕ являются полями, потому что они предоставляют вам механизм для перехвата попыток доступа к полям (путем направления их к методам, если они определены); это, однако, не что иное, как синтаксический сахар над методами получения / установки. Это чрезвычайно удобно, но не меняет основную парадигму - раскрытие полей без контроля доступа к ним нарушает принцип инкапсуляции.

jcdyer
16 октября 2009 в 15:27
16

@ ChssPly76 - я не согласен. У меня такой же контроль, как если бы они были свойствами, потому что я могу сделать их свойствами, когда мне нужно. Нет никакой разницы между свойством, использующим стандартные методы получения и установки, и необработанным атрибутом, за исключением того, что необработанный атрибут работает быстрее, потому что он использует базовый язык, а не вызывает методы. Функционально они идентичны. Единственный способ нарушения инкапсуляции - это если вы считаете, что круглые скобки (obj.set_attr('foo')) по своей природе превосходят знаки равенства (obj.attr = 'foo'). Публичный доступ - это публичный доступ.

Timo Huovinen
10 августа 2013 в 09:49
1

@jcdyer столько контроля да, но не такая удобочитаемость, другие часто ошибочно полагают, что obj.attr = 'foo' просто устанавливает переменную, и больше ничего не происходит

ely
17 октября 2014 в 14:00
9

@TimoHuovinen Чем это отличается от пользователя в Java, если предположить, что obj.setAttr('foo') «просто устанавливает переменную без каких-либо дополнительных действий»? Если это общедоступные методы, то это общедоступный метод. Если вы используете его для достижения некоторого побочного эффекта и он общедоступен, тогда вам лучше иметь возможность рассчитывать на то, что все работает , как если бы произошел только этот предполагаемый побочный эффект все другие детали реализации и другие побочные эффекты, использование ресурсов и т. д., скрытые от опасений пользователя). С Python это абсолютно не отличается. Синтаксис Python для достижения эффекта просто проще.

avatar
Jason Baker
14 октября 2009 в 18:27
2

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

Hubert Grzeskowiak
1 августа 2016 в 09:54
0

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

avatar
Peter D
14 октября 2009 в 18:27
56

Есть много причин. Мне больше всего нравится, когда вам нужно изменить поведение или отрегулировать то, что вы можете установить для переменной. Например, допустим, у вас есть метод setSpeed ​​(int speed). Но вы хотите, чтобы вы могли установить максимальную скорость только 100. Сделайте что-то вроде:

public void setSpeed(int speed) {
  if ( speed > 100 ) {
    this.speed = 100;
  } else {
    this.speed = speed;
  }
}

А что, если ВЕЗДЕ в коде вы использовали публичное поле, а затем поняли, что вам нужно указанное выше требование? Получайте удовольствие от поиска каждого использования публичного поля вместо того, чтобы просто изменять свой сеттер.

Мои 2 цента :)

Nathan Fellman
14 октября 2009 в 18:57
68

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

Hardryv
14 октября 2009 в 19:05
12

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

PostMan
14 октября 2009 в 20:36
4

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

Dean J
14 октября 2009 в 20:39
2

Отслеживание вещей не должно быть проблемой в наши дни; Я использую IDE. Типы проектов, над которыми я работаю, никогда не передаются внешним источникам.

Graeme Perrow
7 ноября 2009 в 22:09
30

@Nathan: Найти других применений - не проблема. Изменение их всех.

Phil
29 октября 2013 в 18:52
2

@GraemePerrow Alt-Shift-R (eclipse - другие IDE могут сделать это по-другому). Бум, все изменилось).

R. Martinho Fernandes
30 октября 2013 в 11:12
24

@GraemePerrow необходимость их всех менять - это преимущество, а не проблема :( Что, если бы у вас был код, предполагающий, что скорость может быть выше 100 (потому что, вы знаете, до того, как вы разорвали контракт, это могло быть!) (while(speed < 200) { do_something(); accelerate(); })

Pacerier
7 августа 2014 в 08:41
0

@GraemePerrow, мы не живем в эпоху блокнотов ... и в будущем, вероятно, будут лучшие IDE, а не более паршивые.

Dennis
6 ноября 2014 в 20:14
0

@Nathan, иногда у вас есть только интерпретатор (PHP), получайте удовольствие, находя их! :)

Emiliano
9 января 2015 в 10:46
8

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

Julian Sievers
8 июля 2015 в 10:10
1

Я не знаю, не было ли в IDE версии 09 этих инструментов рефакторинга, но на сегодняшний день это действительно не проблема. Любая достойная IDE предоставляет метод поля «инкапсулировать поле», в котором общедоступные поля могут быть легко преобразованы в методы получения / установки. Это также изменит все существующие вхождения кода для использования вновь созданных средств доступа. Так что придерживайтесь YAGNI и измените его через несколько секунд, если вам это нужно.

Piotr Aleksander Chmielowski
23 февраля 2016 в 12:24
34

ОЧЕНЬ плохой пример! Кто-то должен позвонить: myCar.setSpeed(157); и через несколько строк speed = myCar.getSpeed(); А теперь ... Я желаю вам счастливой отладки, пытаясь понять, почему speed==100, а должно быть 157

amdev
20 июля 2017 в 09:49
2

Я не думаю, что добавлять логику в вашу модель - хорошая идея. Используйте валидатор или установите максимум из своих сервисов.

Galik
16 марта 2018 в 15:10
1

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

imme
3 мая 2019 в 11:44
0

Разве упомянутое требование не должно быть настройкой экземпляра класса Speed, а не проверкой в ​​классе, который использует Speed? Таким образом, ограничение скорости будет тем, что класс скорости будет отслеживать и применять. Не те классы, которые используют скорость.

avatar
Pete
14 октября 2009 в 18:25
1

Кроме того, это сделано для «будущего» вашего класса. В частности, переход от поля к свойству является нарушением ABI, поэтому, если вы позже решите, что вам нужно больше логики, чем просто «установить / получить поле», тогда вам нужно сломать ABI, что, конечно, создает проблемы для чего угодно. остальное уже скомпилировано для вашего класса.

R. Martinho Fernandes
26 сентября 2012 в 05:54
2

Я полагаю, что изменение поведения геттера или сеттера не является критическим изменением. </sarcasm>

Phil
29 октября 2013 в 19:10
0

@ R.MartinhoFernandes не всегда должен быть таким. TBYS

avatar
John Millikin
14 октября 2009 в 18:25
10

В языках, которые не поддерживают «свойства» (C ++, Java) или требуют перекомпиляции клиентов при изменении полей на свойства (C #), использование методов get / set легче изменить. Например, добавление логики проверки к методу setFoo не потребует изменения открытого интерфейса класса.

В языках, поддерживающих «реальные» свойства (Python, Ruby, может быть, Smalltalk?), Нет смысла получать / устанавливать методы.

steamer25
14 октября 2009 в 19:11
1

Re: C #. Если вы добавите функциональность к get / set, разве это не потребует перекомпиляции?

John Millikin
14 октября 2009 в 19:14
0

@ steamer25: извините, набрал неправильно. Я имел в виду, что клиентов класса придется перекомпилировать.

R. Martinho Fernandes
26 сентября 2012 в 05:57
5

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

Phil
29 октября 2013 в 19:33
0

@ R.MartinhoFernandes, как исправить эту "сломанную" проблему? Компилятор не может сказать, сломалась она или нет. Это вызывает беспокойство только тогда, когда вы пишете библиотеки для других, но вы делаете это как универсальный zOMG, вот драконы!

R. Martinho Fernandes
30 октября 2013 в 11:02
3

Требование перекомпиляции, как упоминалось в ответе, - это один из способов, которым компилятор может сообщить вам о возможном критическом изменении. И почти все, что я пишу, фактически является «библиотекой для других», потому что я работаю не один. Я пишу код, у которого есть интерфейсы, которые будут использовать другие участники проекта. Какая разница? Черт, даже если я буду пользователем этих интерфейсов, почему я должен придерживаться более низких стандартов качества в своем коде? Я не люблю работать с проблемными интерфейсами, даже если их пишу я.

Basil Bourque
29 августа 2018 в 22:23
0

Objective-C использует аннотации для обозначения свойств. Если программист класса не предоставляет методов доступа (получателей / установщиков) для аннотированной переменной-члена, такие методы синтезируются автоматически. Разработчик-программист всегда может вернуться и предоставить реализацию аксессора в любое время, когда он пожелает. Я понятия не имею, почему Java не приняла такое простое решение проблемы отсутствия свойств.

avatar
Justin Niessner
14 октября 2009 в 18:25
6

Один из основных принципов объектно-ориентированного проектирования: Инкапсуляция !

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

sbi
23 августа 2012 в 21:14
23

Геттеры и сеттеры инкапсуляции смехотворно тонкие. См. здесь.

thecoshman
3 мая 2013 в 10:17
0

Если в вашем общедоступном интерфейсе указано, что 'foo' относится к типу 'T' и может иметь любое значение, вы никогда не сможете это изменить. В последнем случае вы не можете решить сделать его типа «Y», а также не можете наложить такие правила, как ограничения по размеру. Таким образом, если у вас есть общедоступные get / set, которые ничего не делают с set / get, вы не получаете ничего, чего не будет предлагать общедоступное поле, что делает его более громоздким в использовании. Если у вас есть ограничение, например, для объекта может быть установлено значение null или значение должно быть в пределах диапазона, тогда да, потребуется метод общедоступного набора, но вы все равно представляете контракт на установку этого значения, которое может ' т изменить

Phil
29 октября 2013 в 20:04
0

Почему нельзя изменить контракт?

R. Martinho Fernandes
30 октября 2013 в 11:06
5

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

avatar
Thomas Owens
14 октября 2009 в 18:25
40

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

Например, если foo был общедоступным, я мог бы легко установить его на null, а затем кто-то другой мог бы попытаться вызвать метод объекта. Но его больше нет! С помощью метода setFoo я мог гарантировать, что для foo никогда не будет установлено значение null.

Аксессоры и мутаторы также допускают инкапсуляцию - если вы не должны видеть значение после его установки (возможно, оно установлено в конструкторе, а затем используется методами, но никогда не должно быть изменено), его никогда не увидят. кем угодно. Но если вы можете разрешить другим классам видеть или изменять его, вы можете предоставить соответствующий аксессор и / или мутатор.

avatar
ChssPly76
14 октября 2009 в 18:23
541

Поскольку через 2 недели (месяцы, годы), когда вы поймете, что вашему установщику нужно сделать больше , чем просто установить значение, вы также поймете, что свойство использовалось непосредственно в 238 других классы :-)

Dean J
14 октября 2009 в 18:24
96

Я сижу и смотрю на приложение на 500 тысяч строк, в котором никогда не было необходимости. Тем не менее, если это понадобится однажды, это вызовет кошмар обслуживания. Достаточно хорошо для меня.

ChssPly76
14 октября 2009 в 18:27
21

Я могу только позавидовать вам и вашему приложению :-) Тем не менее, это также действительно зависит от вашего программного стека. Например, Delphi (и C # - я думаю?) Позволяет вам определять свойства как граждан 1-го класса, где они могут читать / записывать поле напрямую, но - если вам это нужно - делать это также с помощью методов getter / setter. Мучо удобно. Увы, в Java нет - не говоря уже о стандарте javabeans, который заставляет вас также использовать геттеры / сеттеры.

Rowland Shaw
14 октября 2009 в 18:28
1

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

LBushkin
14 октября 2009 в 18:59
16

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

UpTheCreek
14 октября 2009 в 19:05
5

@LBushkin - при условии, что ваш код не будет использоваться публично.

cmcginty
15 июня 2012 в 21:31
69

звучит как доработанная оптимизация

sbi
23 августа 2012 в 21:13
43

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

Erik Reppen
12 июня 2013 в 21:35
3

Эта проблема не является индикатором того, что вам нужно использовать методы set или get. Это показатель того, что всей команде нужно избавиться от IDE и научить писать, понимать и поддерживать ООП без них.

ChssPly76
12 июня 2013 в 21:41
1

@ErikReppen Как я уже говорил выше, это зависит от конкретной языковой реализации. Для тех, где свойства не являются первоклассными гражданами (например, Java), совершенно неважно, используете ли вы какую-то среду IDE или простой vi для написания кода - как только свойство было открыто открыто, вы не можете его вернуть.

Erik Reppen
13 июня 2013 в 14:43
1

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

Erik Reppen
12 августа 2013 в 16:48
0

@ ChssPly76 Я не объяснил смысл удаления IDE. Попробуйте написать код без него, когда вы фактически получили глобальный доступ к одной и той же переменной из 238 мест и теперь должны выяснить, где что-то пошло не так.

Phil
29 октября 2013 в 20:15
4

К чему все разговоры об удалении IDE? Зачем кому-то это делать? Конечно, я знаю, как использовать ed, и иногда это инструмент для работы. Однако написание программного обеспечения - не тот случай.

Zorf
14 октября 2014 в 12:41
1

@sbi Я не понимаю почему? Я имею в виду, что если у вас есть очень простой случай класса «человек», поле будет иметь вид first_name, и, очевидно, вы хотите просто получить это поле дословно в какой-то момент. И вам следует использовать геттер. Возможно, в будущем вы полностью измените реализацию своего класса, чтобы класс асинхронно извлекал это с сервера или делал что-то еще.

sbi
14 октября 2014 в 13:06
3

@Zorf: Если весь ваш person хранит имя и фамилию, то это не что иное, как (прославленный) struct и, следовательно, не ООП, а структурированное программирование. (Получат ли сеттеры и геттеры что-нибудь в этом сценарии из-за простого доступа к данным, в лучшем случае сомнительно: A) Как еще, кроме строки, вы могли бы представить имена людей? И я говорю здесь о практических сценариях, а не о теории, потому что B) за 20 лет практики я никогда не сталкивался с такой ситуацией.)