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

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

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

public String foo;

хуже, чем:

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

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

30 ответов


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

вот некоторые из причин я знаю:

  • инкапсуляция поведения, связанного с получением или установкой свойства - это позволяет более легко добавлять дополнительные функции( например, проверку) позже.
  • скрытие внутреннего представления свойства при отображении свойства с использованием альтернативного представления.
  • изоляция вашего публичного интерфейса от изменений-позволяет публичному интерфейсу оставаться постоянным, пока реализация изменяется, не затрагивая существующих потребителей.
  • управление семантикой управления временем жизни и памятью (утилизация) свойства-особенно важно в неуправляемых средах памяти (например, C++ или Цель-С).
  • предоставление точки перехвата отладки для изменения свойства во время выполнения-отладка, когда и где свойство изменилось на определенное значение, может быть довольно сложным без этого на некоторых языках.
  • улучшена совместимость с библиотеками, которые предназначены для работы с геттером/сеттерами свойств-на ум приходят насмешки, сериализация и WPF.
  • разрешение наследникам изменять семантику поведения свойства и предоставляется путем переопределения методов getter / setter.
  • позволяет геттеру / сеттеру передаваться как лямбда-выражения, а не значения.
  • геттеры и сеттеры могут разрешать разные уровни доступа - например, get может быть общедоступным, но набор может быть защищен.

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


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

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

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

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

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

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

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

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

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

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


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

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

person.name = "Joe";

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

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


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

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


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

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

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

мои 2 цента :)


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

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

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


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

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)

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


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

Я сделал небольшой тест производительности. Я написал класс "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

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


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

  1. когда вы поймете, что вам нужно сделать больше, чем просто установить и получить значение, вы можете просто сделать поле, которое будет сразу сказать, где ты напрямую к нему доступ.
  2. любая проверка, которую вы выполняете там, может быть только контекстной, которая редко выполняется на практике.
  3. вы можете изменить устанавливаемое значение-это абсолютный кошмар, когда вызывающий абонент передает вам значение, которое они [shock horror] хотят, чтобы вы сохранили как есть.
  4. вы можете скрыть внутреннее представление - фантастика, так что вы убедитесь, что все эти операции симметричны правильно?
  5. вы изолировали свой открытый интерфейс от изменений под листами - если вы разрабатывали интерфейс и не были уверены, что прямой доступ к чему-то был в порядке, то вы должны были продолжал конструировать.
  6. некоторые библиотеки ожидают этого, но не многие - отражение, сериализация, макет объектов все работают просто отлично с открытыми полями.
  7. наследуя этот класс, вы можете переопределить функциональность по умолчанию - другими словами, вы можете действительно запутать вызывающих абонентов, не только скрывая реализацию, но и делая ее непоследовательной.

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


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

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

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

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


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

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

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

эти методы печально известный геттер и сеттер....


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

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

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


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

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

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

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

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


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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

мы бы в конечном итоге с чем-то вроде:

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

великолепно! И теперь мы просто меняем бутылку на это:

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;
  public void setCapcityMl(int capacityMl) {
    this.capacityMl = capacityMl;
    checkNotOverFlow();
  }

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

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

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

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

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

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


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

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


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


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

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

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


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

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

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


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

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

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

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

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

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


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

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

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

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


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

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

int getVar() const { return var ; }

так что у вас есть:

doSomething( obj->getVar() ) ;

вместо

doSomething( obj->var ) ;

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

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

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

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

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

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


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

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

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


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

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

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


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

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


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


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


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