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


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

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

public String foo;

хуже, чем:

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

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


1288


источник


Ответы:


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

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

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

786



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


385



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

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

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

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

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

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

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

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

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

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


305



Lots of people talk about the advantages of getters and setters but I want to play devil's advocate. Right now I'm debugging a very large program where the programmers decided to make everything getters and setters. That might seem nice, but its a reverse-engineering nightmare.

Say you're looking through hundreds of lines of code and you come across this:

person.name = "Joe";

It's a beautifully simply piece of code until you realize its a setter. Now, you follow that setter and find that it also sets person.firstName, person.lastName, person.isHuman, person.hasReallyCommonFirstName, and calls person.update(), which sends a query out to the database, etc. Oh, that's where your memory leak was occurring.

Understanding a local piece of code at first glance is an important property of good readability that getters and setters tend to break. That is why I try to avoid them when I can, and minimize what they do when I use them.


73



There are many reasons. My favorite one is when you need to change the behavior or regulate what you can set on a variable. For instance, lets say you had a setSpeed(int speed) method. But you want that you can only set a maximum speed of 100. You would do something like:

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

Now what if EVERYWHERE in your code you were using the public field and then you realized you need the above requirement? Have fun hunting down every usage of the public field instead of just modifying your setter.

My 2 cents :)


44



In a pure object-oriented world getters and setters is a terrible anti-pattern. Read this article: Getters/Setters. Evil. Period. In a nutshell, they encourage programmers to think about objects as of data structures, and this type of thinking is pure procedural (like in COBOL or C). In an object-oriented language there are no data structures, but only objects that expose behavior (not attributes/properties!)

You may find more about them in Section 3.5 of Elegant Objects (my book about object-oriented programming).


42



One advantage of accessors and mutators is that you can perform validation.

For example, if foo was public, I could easily set it to null and then someone else could try to call a method on the object. But it's not there anymore! With a setFoo method, I could ensure that foo was never set to null.

Accessors and mutators also allow for encapsulation - if you aren't supposed to see the value once its set (perhaps it's set in the constructor and then used by methods, but never supposed to be changed), it will never been seen by anyone. But if you can allow other classes to see or change it, you can provide the proper accessor and/or mutator.


36



Depends on your language. You've tagged this "object-oriented" rather than "Java", so I'd like to point out that ChssPly76's answer is language-dependent. In Python, for instance, there is no reason to use getters and setters. If you need to change the behavior, you can use a property, which wraps a getter and setter around basic attribute access. Something like this:

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)

28



Well i just want to add that even if sometimes they are necessary for the encapsulation and security of your variables/objects, if we want to code a real Object Oriented Program, then we need to STOP OVERUSING THE ACCESSORS, cause sometimes we depend a lot on them when is not really necessary and that makes almost the same as if we put the variables public.


22



Thanks, that really clarified my thinking. Now here is (almost) 10 (almost) good reasons NOT to use getters and setters:

  1. When you realize you need to do more than just set and get the value, you can just make the field private, which will instantly tell you where you've directly accessed it.
  2. Any validation you perform in there can only be context free, which validation rarely is in practice.
  3. You can change the value being set - this is an absolute nightmare when the caller passes you a value that they [shock horror] want you to store AS IS.
  4. You can hide the internal representation - fantastic, so you're making sure that all these operations are symmetrical right?
  5. You've insulated your public interface from changes under the sheets - if you were designing an interface and weren't sure whether direct access to something was OK, then you should have kept designing.
  6. Some libraries expect this, but not many - reflection, serialization, mock objects all work just fine with public fields.
  7. Inheriting this class, you can override default functionality - in other words you can REALLY confuse callers by not only hiding the implementation but making it inconsistent.

The last three I'm just leaving (N/A or D/C)...


21