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

И ещё не нужно пытаться возвращать свойства объекта, так как свойства нельзя использовать извне.




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


  • Объект должен быть похож на "живую" сущность;
  • Мы "обращаемся, просим" объект сделать что-то;
  • Свойства объекта — исключительно его собственность;
  • Статические методы — зло;

Ну и цель у этого действа тоже должна быть. Наверняка уже у некоторых был вопрос, мол, мне и так нормально живётся с публичными свойствами и статическими методами, зачем мне это вообще нужно?


Проектировать архитектуру объектов нужно так, чтобы не брать всю обязанность на себя, как в процедурном программировании. Мы должны доверять объекту, знать, что если он сейчас "Вася", то он и в будущем будет "Васей"; если его метод сейчас возвращает "привет", то и в будущем он будет возвращать "привет".


Почти на пальцах объяснил. Спасибо за то, что дочитали =)

Комментарии (11)


  1. Djaler
    29.10.2018 22:40

    И зачем публиковать избранные тезисы из книги Егора Буганко в виде отдельной статьи?


  1. Drag13
    29.10.2018 23:11

    Погодите, как насчет тонких моделей и сервисов которые производят над ними операции, но не имеют внутреннего состояния? Тоже зло?


    1. sentyaev
      30.10.2018 02:54

      как насчет тонких моделей и сервисов которые производят над ними операции

      Ну так это и называется «процедурное программирование».


  1. vintage
    30.10.2018 00:16
    +1

    Поднимемся же на борьбу с объективацией!

    А если серьёзно, то вы безапелляционно заявляете довольно сомнительные вещи. О чём тут спорить?


  1. evocatus
    30.10.2018 00:22

    Если объект состоит только из свойств, у которых, к тому же прописаны setters и getters, то объект здесь, как мне кажется (и не только мне), вообще не нужен. Просто передавайте данные между функциями.

    И вообще: посмотрите, хорошее видео.


    1. dopusteam
      30.10.2018 08:09

      Если слишком много параметров, то иногда лучше всё же сделать объект


      Другой вопрос, что много параметров — это не есть хорошо, но случаи разные бывают


  1. garex
    30.10.2018 03:14

    Автор, я так и не увидел в статье ничего про элегантные объекты. Как их проектировать?


  1. G1yyK
    30.10.2018 07:12

    И что делать теперь с этой информацией?

    Хорошо, сделали мы «элегантные обекты» — а с остальным как жить, выкидывать их, как жить дальше!

    Есть мечты, что мир будет идеальным, а есть задачи реального мира.


  1. babylon
    30.10.2018 08:42

    Ещё вопросы.
    Если вы можете построить из компонентов объект или, наоборот, разобрать объект на составные части это ооп?
    Сколько компонент в таком объекте?


    o: {
        a: {
            d: 0
        },
        b: {
            d: 0
        },
        c: {
            d: 0
        }
    }

    Влияние пропсов друг на друга и способы изоляции такого влияния.


    Свойства объекта — исключительно его собственность;

    Вы отказываете пропсам в возможности менять владельца?
    Видео как и ваша статья ни о чём.


  1. dididididi
    30.10.2018 09:47

    Никогда не понимал, зачем писать пустые геттеры и сеттеры, а не просто сделать поле публичным.


    1. vintage
      30.10.2018 10:32

      Карго-культ.