Статья Егора Бугаенко 19 Сентября, 2014 | Опубликовано в: Core Java Геттеры/Сеттеры. Зло. И точка - 1Этот старый спор начал Аллен Холаб в своей знаменитой статье, еще в 2003 году, Почему методы геттер и сеттер - зло - являются ли геттеры/сеттеры анти-паттерном и стоит ли их избегать, или это то, что нам неминуемо понадобиться в объектно-ориентированном программировании. Добавлю я и свои полкопейки в эту дискуссию. Суть текста ниже вот в чем: геттеры и сеттеры - это плохая практика, у тех, кто их использует нет никаких оправданий. Но опять же, чтобы избежать непонимания, я вовсе не утверждаю, что использование get/set нужно избегать, где возможно. Нет. Я говорю о том, что вы их даже близко к вашему коду не подпускали. Геттеры/Сеттеры. Зло. И точка - 2Как вам такое заявление? Достойно вашего внимания? Вы уже пользуйтесь 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". Объективное Мышление Начните думать как объект и вы сразу же переименуете эти методы. Вот, что возможно у вас получится:
Dog dog = new Dog();
dog.take(new Ball());
Ball ball = dog.give();
Вот теперь мы относимся к собаке как к настоящему животному, которое может взять у нас мячик и может отдать обратно, если мы попросим. На всякий случай замечу, что собака не сможет вернуть NULL. Просто собаки не знают, что такое NULL! Объективное мышление (думание) сразу же убирает NULL references из вашего кода. Геттеры/Сеттеры. Зло. И точка - 3
Рыбка по Имени Ванда (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. Забыл сказать, да, вставка зависимостей через сеттер - это тоже ужасный анти-паттерн. Но об этом в следующем посте! Oригинальная статья