Объектно-ориентированное программирование, наверное, самая популярная парадигма из всех ныне существующих. В топах популярных языков лидируют именно объектно-ориентированные языки, то есть Java, C#, Python, C++. Всё вроде бы классно, объекты есть, классы есть, да и жизнь цветёт другими красками. Однако, почему иногда складывается ощущение, что мы просто привязываем процедуру к какому-то классу? Мы что-то делаем не так?
Суть проблемы
Допустим, была у нас процедура print(String arg)
, которая выводила на экран переданное ей значение. А мы взяли, да создали класс InputOutput
и сделали нашу процедуру методом. Есть кардинальные отличия? Нет. И даже известно почему.
Причины проблемы
Отношение к объектам
С этого и стоит начать. Чем для начинающего программиста в ООП являются объекты? Тем же, чем и для Википедии, набором именованных свойств, которые можно удалять, добавлять, изменять, или вообще ничего с ними не делать. В большинстве своём из этого и вытекают следующие далее причины. Объект — не просто набор каких-то там свойств. Точнее, так и есть, но к объекту нужно относиться как к «живой» сущности. Мы не вызываем метод, мы «вежливо просим объект сделать это». Мы не можем просто так взять и начать выполнять манипуляции со свойствами объекта, так как это только его свойства, мы не можем узнать, что у него «внутри», ибо это его личное дело. Если мы изменим какое-то свойство у объекта, он уже перестанет быть тем, чем был раньше. Разве это можно хотя бы приблизительно назвать «живой» сущностью? Скорее, какой-то конструктор LEGO.
Getters, setters, публичные свойства
Вот это именно то, о чём я и говорил.
// C# class Person { public string Name { get { return name; } set { name = value; } } }
Свойства объекты концептуально не могут быть публичными, это нарушает правило инкапсуляции. Если свойства публичны, то мы имеем не «мыслящий» объект, а глупую структуру данных, которая может только сохранять в себе информацию. Объект должен в себе что-то инкапсулировать. Если он задумался каким-то — пусть он таким и остаётся, это его личное пространство и распоряжаться им может только он в своих методах.
В некоторых языках программирования эту проблему попытались решить, введя data objects, дабы отделить «правильные» объекты от глупых структур данных. Иногда приходится делать и такое. К примеру, Kotlin:
// Kotlin data class Person(val name: String) { var age: Int = 0 }
Помимо сахара в виде ключевого слова data
такие объекты имеют некоторые ограничения и специфичные им методы.
Static методы
Это то, о чём я говорил в начале.
Статические методы это методы, которые не привязаны к конкретному экземпляру объекта.
Мы просто вставили нашу процедуру в объект, и всё. Эта функция не использует свойства объекта, можно даже экземпляр не создавать. Должен сказать, что такое бывает полезно, если нужно вынести функции в модули. Однако это модули, а не объекты. Мы же хотим ООП, а не процедурное программирование?
// Java class Program { public static void main(){ /* Точка входа main() вынуждена являться статической */ System.out.println("hello"); } }
NULL
Тоже своего рода проблема. Язык Kotlin, к примеру, пытается с ней бороться. Но полностью, этого, конечно, не сделаешь, поэтому тип Null
в этом языке тоже есть. Однако его следует избегать в ООП. Метод не должен быть процедурой. Если он ничего не возвращает, то он должен хотя бы что-то инкапсулировать. К примеру:
# Python # Метод достаёт из БД имя и обновляет его в соответствующем свойстве. # ВАЖНО: НЕ МЫ ОБНОВЛЯЕМ, А САМ МЕТОД! # Мы не передаем параметр, а объект сам понимает, что ему делать. class User: def update_name(self): # ... some code ... # self.name = name
И ещё не нужно пытаться возвращать свойства объекта, так как свойства нельзя использовать извне.
Надеюсь, что я объяснил хоть немного приемлемо, потому что тема специфичная, в самый раз для каверзных вопросов мне и хорошего такого холивара. Подводя итог, выделю самое важное:
- Объект должен быть похож на «живую» сущность;
- Мы «обращаемся, просим» объект сделать что-то;
- Свойства объекта — исключительно его собственность;
- Статические методы — зло;
Ну и цель у этого действа тоже должна быть. Наверняка уже у некоторых был вопрос, мол, мне и так нормально живётся с публичными свойствами и статическими методами, зачем мне это вообще нужно?
Проектировать архитектуру объектов нужно так, чтобы не брать всю обязанность на себя, как в процедурном программировании. Мы должны доверять объекту, знать, что если он сейчас «Вася», то он и в будущем будет «Васей»; если его метод сейчас возвращает «привет», то и в будущем он будет возвращать «привет».
Почти на пальцах объяснил. Спасибо за то, что дочитали =)
ссылка на оригинал статьи https://habr.com/post/428157/
Добавить комментарий