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

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

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

public String foo;

хуже, чем:

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

В то время как первый принимает гораздо меньший шаблонный код.

Ответ 1

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

Вот некоторые из причин, о которых я знаю:

  • Инкапсуляция поведения, связанного с получением или настройкой свойства, - это позволяет добавить дополнительную функциональность (например, проверку) позже.
  • Скрытие внутреннего представления свойства при экспонировании свойства с использованием альтернативного представления.
  • Изоляция вашего открытого интерфейса от изменения - позволяя публичному интерфейсу оставаться постоянным, а реализация меняется, не затрагивая существующих потребителей.
  • Управление семантикой объекта управления временем жизни и памяти (удаление) - особенно важно в средах с неконтролируемой памятью (например, С++ или Objective-C).
  • Предоставление точки перехвата отладки при изменении свойства во время выполнения - отладка, когда и где свойство, измененное на конкретное значение, может быть довольно сложно без этого на некоторых языках.
  • Взаимодействие с библиотеками, которые предназначены для работы с атрибутами getter/seters - Mocking, Serialization и WPF, приходят на ум.
  • Предоставление наследникам возможности изменять семантику поведения объекта и подвергается переопределению методов getter/setter.
  • Разрешить использование getter/setter в качестве лямбда-выражений, а не значений.
  • Getters и seters могут разрешать разные уровни доступа - например, get может быть общедоступным, но набор может быть защищен.

Ответ 2

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

Ответ 3

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

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

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

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

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

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

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

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

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

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

Ответ 4

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

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

person.name = "Joe";

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

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

Ответ 5

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

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

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

Мои 2 цента:)

Ответ 6

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

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

Ответ 7

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

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

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

Ответ 8

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

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)

Ответ 9

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

Ответ 10

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

  • Когда вы понимаете, что вам нужно сделать больше, чем просто установить и получить значение, вы можете просто сделать поле частным, что сразу сообщит вам, где вы напрямую обращались к нему.
  • Любая проверка, которую вы выполняете, может быть только контекстной, что редко бывает на практике.
  • Вы можете изменить установленное значение - это абсолютный кошмар, когда вызывающий абонент передает вам значение, которое они [shock horror] хотят, чтобы вы сохранили AS IS.
  • Вы можете скрыть внутреннее представление - фантастическое, так что вы убедитесь, что все эти операции симметричны правильно?
  • Вы изолировали свой публичный интерфейс от изменений под листами - если вы разрабатывали интерфейс и не были уверены, что прямой доступ к чему-то был в порядке, то вы должны были продолжать проектирование.
  • Некоторые библиотеки ожидают этого, но не так много - отражение, сериализация, mock-объекты все отлично работают с публичными полями.
  • Наследуя этот класс, вы можете переопределить функции по умолчанию - другими словами, вы можете ДЕЙСТВИТЕЛЬНО путать вызывающих, не только скрывая реализацию, но и делая ее несовместимой.

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

Ответ 11

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

Я сделал небольшой тест производительности. Я написал класс "NumberHolder", который, ну, имеет целое число. Вы можете прочитать это Integer с помощью метода getter 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

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

Ответ 12

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

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

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

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

Ответ 13

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

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

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


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

Мандат инкапсуляции должен делать частные и общедоступные поля.

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

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

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

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

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

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) с помощью методов getter и setter. Поэтому завтра мы можем просто создавать объекты этого bean class(Person class), когда нам нужно добавить нового человека (см. Рисунок). Таким образом, мы повторно используем поля и методы класса bean, что намного лучше.

Ответ 14

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

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

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

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

    protected YourType _yourName = null;
    public YourType YourName{
      get
      {
        if (_yourName == null)
        {
          _yourName = new YourType();
          return _yourName;
        }
      }
    }

Ответ 15

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

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

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

Эти методы - позорный геттер и сеттер....

Ответ 16

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

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

Ответ 17

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

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

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

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

Большой! И теперь мы просто изменим бутылку на это:

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, объявив его окончательным. Но если бы это был список, вы могли бы очистить его, добавить в него новые элементы и так далее. Вы не можете ограничить доступ к прикосновению к внутренностям.

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

Ответ 18

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

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

Ответ 19

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

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

Ответ 20

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

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

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


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

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

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


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

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

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

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

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

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

Ответ 21

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

Ответ 22

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

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

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

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

Ответ 23

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

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

int getVar() const { return var ; }

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

doSomething( obj->getVar() ) ;

Вместо

doSomething( obj->var ) ;

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

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

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

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

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

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

Ответ 24

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

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

Ответ 25

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

  • Абстракция
  • Герметизация

Предположим, что у нас есть класс 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();
    }
 }

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

Ответ 26

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

Ответ 27

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

Ответ 28

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

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

Ответ 29

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

Ответ 30

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