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

от автора

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. Я не радикал, а за осмысленное и прагматичное использование объектов: для тривиальной логики можно оставить циклы, разрешаю)

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Эта мысль банальна или хороший совет?
37.8% Хороший совет 31
62.2% Банальщина 51
Проголосовали 82 пользователя. Воздержались 30 пользователей.

ссылка на оригинал статьи https://habr.com/ru/post/688348/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *