Элегантные объекты

от автора

Объектно-ориентированное программирование, наверное, самая популярная парадигма из всех ныне существующих. В топах популярных языков лидируют именно объектно-ориентированные языки, то есть 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/