Вопрос-ответ

Why use getters and setters/accessors?

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

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

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

public String foo;

чем хуже, чем:

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

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

Переведено автоматически
Ответ 1

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

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


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

  • Скрытие внутреннего представления свойства при одновременном предоставлении свойства с использованием альтернативного представления.

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

  • Управление временем жизни и семантикой управления памятью (disposal) свойства - особенно важно в средах без управления памятью (таких как C ++ или Objective-C).

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

  • Улучшена совместимость с библиотеками, которые предназначены для работы с средствами получения / установки свойств - на ум приходят Mocking, сериализация и WPF.

  • Позволяет наследникам изменять семантику поведения свойства и предоставлять доступ к нему путем переопределения методов получения / установки.

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

  • Геттеры и установщики могут разрешать разные уровни доступа - например, get может быть общедоступным, но набор может быть защищен.

Ответ 2

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

Ответ 3

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

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

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

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

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

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

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

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


Клиент: "Что я могу сделать с объектом этого класса?"

Дизайнер: "Вы можете читать и записывать несколько переменных".

Клиент: "О ... круто, я полагаю?"


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

Ответ 4

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

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

person.name = "Joe";

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

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

java oop