Как стать автором
Обновить

Что такое объект

Время на прочтение5 мин
Количество просмотров16K

TL;DR

Всем привет!

Недавно работал над задачей. Нужно было получить из сети некоторые объекты по REST и обработать.

Ну все вроде бы ничего сложного. Загрузил, спарсил, вернул. Ок. Затем нужно было полученный массив обработать. Вкратце, по особой логике просуммировать некоторые поля - это могла быть строка, число или null. Начал делать как обычно: создал переменную sum, начал цикл for, в нем начал заниматься основной логикой. Закончил.

Продолжил кодить. Хоба! Эта же логика. Не стал копипастить, вынес в отдельную функцию. Тоже все хорошо.

Начал заниматься 3 задачей. Объединить результаты нескольких вычислений. Опять циклом начал перебирать. Но тут появилась мысль:

“А что, если создать для этого отдельный объект?”

Да нет, чушь! Ведь не существует в реальном мире ОбъединителяКакогоТоРезультата. Но что, если сделать? Попробуем.

Какого!!!?? Почему все вмиг стало так просто? Передал в конструктор нужные объекты и сделал методы, которые применяли свою логику к содержащимся в них объектам. Всего-лишь несколько строчек! Почему я так раньше не делал?

Я раззадорился. Начал видеть объекты везде. Это очень удобно: не нужно смотреть на каждый фрагмент кода с мыслью “а было ли это где нибудь раньше?”. А как тестировать легче стало!

Тут до меня дошло, что было со мной не так: 

Я не разграничивал объекты реального мира и объекты в понимании ООП.

Объекты ООП != Объекты реального мира

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

  • DTO

  • Объекты из реального мира

  • Объекты, реализующие какой-то интерфейс (обычно для запросов по сети, для использования в DI контейнера)

Оглядевшись назад понял, что все дороги вели именно к такому мышлению:

  • В вузе нас учили ООП по каким-то моделям типа: “Вот это объект Человек. У него есть атрибуты Имя и Возраст”, а когда дело доходило до программирования, никто не смотрел как мы пишем код. Получалась каша из императивного программирования и набросков объектов.

  • Во всяких обучающих ресурсах (видео, книги, курсы) дают слишком простые примеры. Примеры слишком прямолинейные (как в выше перечисленном вузе). Не дают почувствовать мощь объектов.

  • Если были задачи, то слишком простые. Не тот уровень сложности, чтобы действительно над чем-то задуматься (например, приевшийся калькулятор). Они не показывали, что объекты могли бы решить многие проблемы.

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

Пожалуй единственное, что меня ограничивало - идефикс, того, что объекты должны представлять концепции реального мира. Кто мне вообще это сказал?

Диаграммы мешают в понимании ООП

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

Взято с https://medium.com/@uferesamuel/uml-class-diagrams-the-simple-approach-eee2d1ffc125
Взято с https://medium.com/@uferesamuel/uml-class-diagrams-the-simple-approach-eee2d1ffc125

ER диаграммы тоже хороши - они слишком сильно сцеплены с реальным миром. Там почти все представляет объекты реального мира.

Взято с https://online.visual-paradigm.com/diagrams/templates/chen-entity-relationship-diagram/see-doctor-erd-chen-notation/
Взято с https://online.visual-paradigm.com/diagrams/templates/chen-entity-relationship-diagram/see-doctor-erd-chen-notation/

Поразмыслив, я понял 3 вещи:

  1. ER диаграмма ничего не имеет общего с ООП - это инструмент для бизнес-анализа. Я не обязан создавать такие же классы, как и на этой диаграмме. Кто мне такое сказал? 

  2. UML показывает высокоуровневую структуру программы: кто в ней есть и что они должны делать/иметь. Т.е. что делать, а не как делать. Реализация ложится на плечи программиста (спойлер, это будут методы на 100+ строк из циклов, условий и других прелестей)

  3. Многие нотации ориентированы для простого понимания концепций программы - из каких компонентов состоит. Ничто не мешает нам вместо классов передавать массивы object. Не нужно ориентироваться на них как на истину в первой инстанции.

В итоге заканчиваем, тем что имеем много объектов. Ура, ООП! А что внутри? Громадные циклы на десятки строк, множество флагов и if’ов - полная императивщина. 

Да о чем я говорю?

Что же я понял? Например,

public interface IWorkingScheduleService
{
    // Возвращает тип дня: рабочий, предпраздничный, праздничный, выходной
    int GetDayType(DateOnly date);
}
// Количество рабочих часов на каждый день недели
public class UserSchedule
{
    public float Monday { get; set; }
    public float Tuesday { get; set; }
    public float Wednesday { get; set; }
    public float Thursday { get; set; }
    public float Friday { get; set; }
    public float Saturday { get; set; }
    public float Sunday { get; set; }
}

Задача - посчитать общее время рабочих часов.
Банально, да? Давайте сделаем функции:

public static class ScheduleHelpers
{
    public static float GetTotalWorkingHours(IWorkingScheduleService service,
                                             UserSchedule schedule,
                                             DateOnly from,
                                             DateOnly to)
    
    {
        // Какая-то логика
        return 0;
    }

    public static float GetTotalWorkingHoursWithoutPreholiday(IWorkingScheduleService service,
                                                              UserSchedule schedule,
                                                              DateOnly from,
                                                              DateOnly to)
    
    {
        // Какая-то логика
        return 0;
    }

    public static float GetTotalHolidayWorkingHours(IWorkingScheduleService service,
                                                    UserSchedule schedule,
                                                    DateOnly from,
                                                    DateOnly to)
    
    {
        // Какая-то логика
        return 0;
    }
}

Но тут мы заметим общую начальную часть: IWorkingScheduleService service, UserSchedule schedule. Почему бы нам не вынести эту логику в отдельный объект?

public class WorkingScheduleCalculator
{
    private readonly IWorkingScheduleService _service;
    private readonly UserSchedule _schedule;

    public WorkingScheduleCalculator(IWorkingScheduleService service, 
                                     UserSchedule schedule)
    
    {
        _service = service;
        _schedule = schedule;
    }
    
    public float GetTotalWorkingHours(DateOnly from, 
                                      DateOnly to)
    
    {
        // Какая-то логика
        return 0;
    }

    public float GetTotalWorkingHoursWithoutPreholiday(DateOnly from, 
                                                       DateOnly to)
    
    {
        // Какая-то логика
        return 0;
    }

    public float GetTotalHolidayWorkingHours(DateOnly from, 
                                             DateOnly to)
    
    {
        // Какая-то логика
        return 0;
    }
}

Как же стало удобно! Все находится рядом, сигнатуры стали короче и поддержка автодополнения в подарок - прелесть!

Выводы

Что я вынес из всего этого?

  1. Объект это не концепция реального мира. Можно сделать объект который имеет имя, атрибуты, поведение, как у объекта реального мира, сделать максимально похожим, но это НЕ ОБЪЕКТ РЕАЛЬНОГО МИРА. Надо прекратить думать в данном ключе!

    Объект - это (всего лишь) данные и функции, ассоциированные с ними 

  2. На каждый блок с логикой (цикл, последовательность условий и т.д.) я смотрю с мыслью: “Нельзя ли вынести это в отдельный объект?”

  3. Таким же образом, смотрю на функции, которые принимают одинаковые аргументы. Их всех можно объединить в объекты, атрибутами которых являются эти общие аргументы.

P.S. Я не радикал, а за осмысленное и прагматичное использование объектов: для тривиальной логики можно оставить циклы, разрешаю)

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Эта мысль банальна или хороший совет?
40.91% Хороший совет63
59.09% Банальщина91
Проголосовали 154 пользователя. Воздержались 72 пользователя.
Теги:
Хабы:
Если эта публикация вас вдохновила и вы хотите поддержать автора — не стесняйтесь нажать на кнопку
Всего голосов 21: ↑10 и ↓11+1
Комментарии81

Публикации

Истории

Ближайшие события

27 августа – 7 октября
Премия digital-кейсов «Проксима»
МоскваОнлайн
20 – 22 сентября
BCI Hack Moscow
Москва
24 сентября
Конференция Fin.Bot 2024
МоскваОнлайн
24 сентября
Astra DevConf 2024
МоскваОнлайн
25 сентября
Конференция Yandex Scale 2024
МоскваОнлайн
28 – 29 сентября
Конференция E-CODE
МоскваОнлайн
28 сентября – 5 октября
О! Хакатон
Онлайн
30 сентября – 1 октября
Конференция фронтенд-разработчиков FrontendConf 2024
МоскваОнлайн
3 – 18 октября
Kokoc Hackathon 2024
Онлайн
7 – 8 ноября
Конференция byteoilgas_conf 2024
МоскваОнлайн