The following text is a partial translation of the original English article, performed by ChatGPT (gpt-3.5-turbo) and this Jekyll plugin:
Существует старая дискуссия, начатая в 2003 году Алленом Холубом в его знаменитой статье Почему методы get и set являются злом, о том, является ли получение/установка значение анти-паттерном, которого следует избегать, или же это нечто, что необходимо в объектно-ориентированном программировании. Я постараюсь внести свое мнение в эту дискуссию.
Суть следующего текста заключается в том, что использование методов получения и установки значения - ужасная практика, и тех, кто ее использует, нельзя оправдать. Еще раз, чтобы избежать недоразумений, я не говорю, что get/set следует избегать при возможности. Нет. Я говорю о том, что они никогда не должны присутствовать в вашем коде.
Вы достаточно высокомерны, чтобы привлечь ваше внимание? Вы используете этот шаблон получения/установки значения уже 15 лет и являетесь уважаемым архитектором Java? И вам не хочется слушать эту ерунду от незнакомца? Я понимаю ваши чувства. Я почти то же самое чувствовал, когда наткнулся на книгу Мышление в объектах Дэвида Уэста, лучшую книгу об объектно-ориентированном программировании, которую я когда-либо читал. Так что, пожалуйста, успокойтесь и попытайтесь понять, пока я пытаюсь объяснить.
Есть несколько аргументов против “аксессоров” (другое название для геттеров и сеттеров) в объектно-ориентированном мире. Я считаю, что все они недостаточно сильны. Давайте кратко рассмотрим их.
Говори, не спрашивай Аллен Холуб говорит: “Не спрашивайте информацию, которая вам нужна для работы; попросите объект, который содержит эту информацию, сделать работу за вас”.
Нарушение принципа инкапсуляции Объект может быть разрушен другими объектами, так как они могут внедрять в него новые данные через сеттеры. Объект просто не может достаточно безопасно инкапсулировать свое состояние, так как кто угодно может его изменить.
Раскрытие деталей реализации Если мы можем получить объект из другого объекта, мы слишком сильно полагаемся на детали реализации первого объекта. Если завтра он изменит, скажем, тип результата, нам придется также изменить наш код.
Все эти обоснования имеют смысл, но они упускают главный момент.
Большинство программистов считают, что объект - это структура данных с методами. Я процитирую статью Божидара Божанова Getters and Setters Are Not Evil.
Это неправильное представление является следствием огромного недоразумения! Объекты не являются «простыми хранилищами данных». Объекты не являются структурами данных с прикрепленными методами. Эта концепция «хранилища данных» перекочевала в объектно-ориентированное программирование из процедурных языков, особенно из C и COBOL. Я повторю еще раз: объект не является набором элементов данных и функций, которые их обрабатывают. Объект не является сущностью данных.
A Ball and A Dog
В истинном объектно-ориентированном программировании объекты являются живыми существами, как вы и я. Они являются живыми организмами со своим собственным поведением, свойствами и жизненным циклом.
Может ли живой организм иметь сеттер? Можно ли “установить” мяч у собаки? На самом деле нет. Но именно это делает следующее программное обеспечение:
Как это звучит?
Можно ли достать мяч от собаки? Ну, вероятно, можно, если она его съела и вы проводите хирургическую операцию. В этом случае, да, мы можем “достать” мяч от собаки. Вот о чем я говорю:
Или даже еще более нелепый пример:
Можете ли вы вообразить эту транзакцию в реальном мире?
Похоже ли это на то, что вы пишете каждый день? Если да, то вы процедурный программист. Признайтесь. И вот что Дэвид Уэст говорит об этом на странице 30 своей книги:
Вам нужна лоботомия? Что ж, мне точно она была нужна, и я получил ее, когда читал Object Thinking Уэста.
Начните думать, как объект, и вы сразу же переименуете эти методы. Вот что, вероятно, вы получите:
Теперь мы относимся к собаке как к настоящему животному, которое может взять мяч у нас и вернуть его по нашей просьбе. Стоит отметить, что собака не может вернуть NULL
. Собаки просто не знают, что такое NULL
. Объектное мышление сразу же исключает нулевые ссылки из вашего кода.
Кроме того, объектное мышление приведет к неизменяемости объектов, как в примере с “весом собаки”. Вы бы переписали это так:
Собака - это неизменный живой организм, которому никто снаружи не может изменить вес, размер, имя и т. д. По запросу она может сообщить свой вес или имя. Нет ничего плохого в публичных методах, которые демонстрируют доступ к определенным “внутренностям” объекта. Но эти методы не являются “геттерами” и никогда не должны иметь префикс “get”. Мы не “получаем” ничего от собаки. Мы не получаем ее имя. Мы просим ее сказать нам свое имя. Видите разницу?
Мы не говорим здесь о семантике. Мы различаем процедурный подход к программированию от объектно-ориентированного. В процедурном программировании мы работаем с данными, манипулируем ими, получаем, устанавливаем и удаляем их при необходимости. Мы владеем ситуацией, а данные являются пассивным компонентом. Собака не значит ничего для нас - она просто “носитель данных”. У нее нет своей собственной жизни. Мы свободны получать все необходимое от нее и устанавливать любые данные в нее. Так работают (работали) языки программирования C, COBOL, Pascal и многие другие процедурные языки.
Наоборот, в истинно объектно-ориентированном мире мы относимся к объектам как к живым организмам, с их собственной датой рождения и моментом смерти - с их собственной идентичностью и привычками, если хотите. Мы можем попросить собаку предоставить нам некоторые данные (например, ее вес), и она может вернуть нам эту информацию. Но мы всегда помним, что собака является активным компонентом. Она решает, что произойдет после нашего запроса.
Поэтому концептуально некорректно иметь методы, начинающиеся с set
или get
в объекте. И дело не в нарушении инкапсуляции, как многие утверждают. Здесь вопрос в том, думаете ли вы как объект или все еще пишете на COBOL синтаксисе на Java.
P.S. Да, вы можете спросить, - а что насчет JavaBeans, JPA, JAXB и многих других Java API, которые полагаются на обозначение get/set? Что насчет встроенной в Ruby функции, которая упрощает создание аксессоров? Что ж, все это наше несчастье. Гораздо проще остаться в примитивном мире процедурного COBOL, чем по-настоящему понять и оценить прекрасный мир истинных объектов.
P.P.S. Забыл сказать, да, внедрение зависимостей через сеттеры также является ужасным анти-паттерном. Об этом я расскажу в одном из следующих постов.
P.P.P.S. Вот что я предлагаю использовать вместо геттеров: принтеры.
Translated by ChatGPT gpt-3.5-turbo/42 on 2023-11-28 at 14:40