• ,

Геттеры/Сеттеры. Зло. И точка.

статья Егора Бугаенко 19 Сентября, 2014 | Опубликовано в: Core Java

Этот старый спор начал Аллен Холаб в своей знаменитой статье, еще в 2003 году, Почему методы геттер и сеттер — зло — являются ли геттеры/сеттеры анти-паттерном и стоит ли их избегать, или это то, что нам неминуемо понадобиться в объектно-ориентированном программировании. Добавлю я и свои полкопейки в эту дискуссию.

Суть текста ниже вот в чем: геттеры и сеттеры — это плохая практика, у тех, кто их использует нет никаких оправданий. Но опять же, чтобы избежать непонимания, я вовсе не утверждаю, что использование get/set нужно избегать, где возможно.

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


Как вам такое заявление? Достойно вашего внимания?

Вы уже пользуйтесь get/set паттерном 15 лет и вы уважаемый Java архитектор? И не хотите даже слушать эту чепуху от незнакомца? Ну… я понимаю ваши чувства. Я чувствовал тоже самое, пока ни наткнулся на книгу Дэвида Уэста «Object Thinking» — это самая лучшая книга по объектно-ориентированному программированию, которую я когда-либо читал. Поэтому, пожалуйста. Успокойтесь и попробуйте понять, что я пытаюсь объяснить.

Предмет Спора

Есть несколько аргументов против «аксессоров» (другое название геттеров и сеттеров) в объектно-ориентированном мире. И все они — очень правильные аргументы. Давайте кратко их рассмотрим.
Спрашивай, Не Говори:
Аллен Холаб говорит: «Не проси информацию, которая вам нужна для работы; „проси“ объект, у которого есть эта информация сделать работу за вас.»

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

Раскрытые Детали Реализации: Если вы можете достать (get) один объект из другого объекта, тогда получается, что мы слишком надеемся на детали реализации первого объекта. Если завтра он изменится (к примеру тип результата), то нам придется изменять код.

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

Основное Заблуждение

Большинство программистов верят, что объект — это структура данных с методами. Я цитирую статью Божидара Божанова: Геттеры и Сеттеры — не зло.
Но большинство объектов, для которых создаются геттеры и сеттеры просто содержат в себе данные.
Это заблуждение является результатом огромного непонимания! Объекты — не «просто хранят данные». Объекты — не структуры данных с прикрепленными методами. Эта концепция «хранения данных» пришла в объектно-ориентированное программирование их процедурных языков, особенно таких как C и COBOL. Я снова повторю: объект — не просто набор элементов данных и функции, которые ими манипулируют. Объект — это не объект данных.
Тогда что?

Мячик и Собака
В настоящем объектно-ориентированном программировании объекты — живые существа, как вы и я. Они живые организмы, со своим собственным поведением, свойствами и циклом жизни.
Может живой организм иметь сеттер? Вы можете прицепить (“set”) мячик к собаке? Не думаю. Но ровно это и делает кусок кода ниже:

Dog dog = new Dog();
dog.setBall(new Ball());


Ну и как это вам?
Вы можете достать (“get ”) мячик из собаки? Ну, положим, что вы сможете. В случае, если она его съела и вы сделали ей операцию. В этом случае, да, вы сможете достать (“get”) мячик из собаки. Это как раз то, о чем я говорю:

Dog dog = new Dog();
Ball ball = dog.getBall();


Или еще более нелепый пример:

Dog dog = new Dog();
dog.setWeight("23kg");


Вы такое в реальной жизни себе можете представить?
Похоже на то, что вы пишете каждый день? Если да, то вы — процедурный программист. Просто признайтесь в этом. А вот, что говорит Дэвид Уэст на странице 30 своей книги:
Первым шагом в трансформации успешного процедурного разработчика в успешного объективного разработчика -это лоботомия.

Вам нужна лоботомия? Мне точно была нужна, и я ее получил, пока читал книгу Уэста «Object Thinking».

Объективное Мышление
Начните думать как объект и вы сразу же переименуете эти методы. Вот, что возможно у вас получится:

1	Dog dog = new Dog();
2	dog.take(new Ball());
3	Ball ball = dog.give();

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


Рыбка по Имени Ванда (1988) Чарльза Кричтона

Кроме того, объективное мышление приведет к неизменяемости (immutability) объекта, такого как «вес собаки» в нашем примере. Вы бы переписали код примерно так:

Dog dog = new Dog("23kg");
int weight = dog.weight();


Собака- это неизменяемый живой организм, который не позволит никому снаружи менять ее вес, или размер, или имя, и т.п. Она может «сообщить», по запросу, ее вес или имя. Нет ничего плохого в публичных методах, которые отображают запросы определенных «внутренних» свойств объекта. Но эти методы не «геттеры» и они никогда не должны получать префикс “get”. Мы не «достаем» (“getting”) из собаки. Мы не получаем ее имя. Мы просим ее сказать нам ее имя.
Видите разницу?

Мы даже не о семантике тут говорим. Мы дифференцируем процедурный подход к программированию от объектно-ориентированного. В процедурном программировании мы работаем с данными, манипулируем ими, получаем (get), и устанавливаем (set), и удаляем, если нужно. Мы руководим, а данные — просто пассивный компонент. Собака для нас ничто — она просто «содержит данные». У нее нет своей жизни. Мы можем свободно получить (get ) все, что нужно от нее и поместить (set) любые данные в нее. Так работают (работали) C, COBOL, Pascal и другие процедурные языки.

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

И именно поэтому — это абсолютно неверно, когда методы объекта начинаются с set или get. И это даже не о нарушении инкапсуляции, как многие думают. Это о том, что либо вы думаете как объект или вы все еще пишете на COBOL с синтаксисом Java.

PS. И да, вы можете спросить: «А что по поводу JavaBeans, JPA, JAXB и многих других Java APIs, которые зависят от get/set?» А что по поводу встроенной функции в Ruby, которая упрощает создание акксессоров? Ну что вам сказать… вам не повезло. Намного легче оставаться в примитивном мире процедурного COBOL, чем понять и принять прекрасный мир настоящих объектов.
PPS. Забыл сказать, да, вставка зависимостей через сеттер — это тоже ужасный анти-паттерн. Но об этом в следующем посте!

оригинальная статья

9 комментариев

CynepHy6
  • CynepHy6
  • +2
  • Комментарий отредактирован 2014-12-17 02:48:11 пользователем CynepHy6
плохая статья. только добавляет непонимания и безобразия в мой хаос java-знаний.
насчет собаки. что если собака это герой в игре и по мере прокачки он увеличивает свои параметры. каждый раз создавать новую собаку? или по воздействием каких-то сил его параметры изменяются. что опять new Собака и куча параметров в конструкторе?
MindIbniM
Судя по общей риторике статьи, автор вероятно предложил бы назвать метод increaseWeight(). Другой вопрос, что, ИМХО, это только ухудшит читаемость кода. К тому же, в каждой более-менее крупной конторе есть определенные стилистические нормы написания кода, которые практически всегда включают требование именования методов доступа и модификации именно через get и set префиксы.
Sdu
Нет, не верный вывод. Смысл в том, что конструктор задает первоначальное состояние, дальнейшее изменение происходит только через внутренние механизмы класса, а не через внешнюю «установку».
Но всегда помните, что собака -это активный компонент. Она решает что случиться после запроса.
Т.е. к примеру мы должны увеличить вес собаки, вместо того, чтобы сделать set и указать новый вес, мы можем «покормить» собаку, а уж в зависимости от метода «кормления» увеличится вес собаки. Как я уже писал в соседней ветке, все это словоблудие, и в плане кодирования поменяется только имя метода. Но, видимо, автор считает что ООП должно более натурально отражать реальность.
CynepHy6
дальше. Собака — это сущность базы данных (база данных собак). есть поля: рост, вес, возраст, кличка, дети, родители, хозяин, адрес проживания, любимые игры, характер. тут тоже устраивать игры с кормежкой собаки и даванием мячика?
вывод для себя сделал — статья бесполезная и вредная, написана чтобы запутать и сбить молодого разработчика с пути)
alexnjc
То уже для опытных прогеров. Остальным только усложнит жизнь.
zharenkov
а как же, например, фреймворки типа seam, где используются java бины, у которых по спецификации должны быть гетеры-сетеры? а hibernate и прочие прелести маппинга в бд? с таким подходом код станет нечитаем и несопровождаем на мой взгляд. А в некоторых случаях и вовсе не взлетит.
maks1m
Зачем было мешать мух и котлеты?!
Все что здесь предлагается это просто игра с именами методов не всегда применимая на практике.
Новичкам вообще не читать!!!
В большинстве своем мы оперируем объектами как невоодушевленными сущностями и эта излишняя вежливость к объектам, как по мне, ни к чему. Я получил из базы документ (объект), поработал с ним и записал его назад в базу, зачем мне цирк устраивать?
fatfaggy
  • fatfaggy
  • 0
  • Комментарий отредактирован 2015-08-08 00:55:35 пользователем fatfaggy
Она решает что случиться после запроса.

извините, но режет глаз)
sokolov
Есть у кого-то Object Thinking в русском переводе, понимаю, что подобную литературу нужно читать в оригинале, но как-то очень тяжело идет оригинал и тема не простая. Заранее спасибо поделившемуся!
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.