TL;DR
Всем привет!
Недавно работал над задачей. Нужно было получить из сети некоторые объекты по REST и обработать.
Ну все вроде бы ничего сложного. Загрузил, спарсил, вернул. Ок. Затем нужно было полученный массив обработать. Вкратце, по особой логике просуммировать некоторые поля - это могла быть строка, число или null. Начал делать как обычно: создал переменную sum, начал цикл for, в нем начал заниматься основной логикой. Закончил.
Продолжил кодить. Хоба! Эта же логика. Не стал копипастить, вынес в отдельную функцию. Тоже все хорошо.
Начал заниматься 3 задачей. Объединить результаты нескольких вычислений. Опять циклом начал перебирать. Но тут появилась мысль:
“А что, если создать для этого отдельный объект?”
Да нет, чушь! Ведь не существует в реальном мире ОбъединителяКакогоТоРезультата. Но что, если сделать? Попробуем.
Какого!!!?? Почему все вмиг стало так просто? Передал в конструктор нужные объекты и сделал методы, которые применяли свою логику к содержащимся в них объектам. Всего-лишь несколько строчек! Почему я так раньше не делал?
Я раззадорился. Начал видеть объекты везде. Это очень удобно: не нужно смотреть на каждый фрагмент кода с мыслью “а было ли это где нибудь раньше?”. А как тестировать легче стало!
Тут до меня дошло, что было со мной не так:
Я не разграничивал объекты реального мира и объекты в понимании ООП.
Объекты ООП != Объекты реального мира
Наверное главной моей ошибкой был недостаток практики: я много интересовался, читал, смотрел, но до кодирования руки не доходили. Поэтому, к моменту того события в моей голове было только 3 паттерна использования объектов:
DTO
Объекты из реального мира
Объекты, реализующие какой-то интерфейс (обычно для запросов по сети, для использования в DI контейнера)
Оглядевшись назад понял, что все дороги вели именно к такому мышлению:
В вузе нас учили ООП по каким-то моделям типа: “Вот это объект Человек. У него есть атрибуты Имя и Возраст”, а когда дело доходило до программирования, никто не смотрел как мы пишем код. Получалась каша из императивного программирования и набросков объектов.
Во всяких обучающих ресурсах (видео, книги, курсы) дают слишком простые примеры. Примеры слишком прямолинейные (как в выше перечисленном вузе). Не дают почувствовать мощь объектов.
Если были задачи, то слишком простые. Не тот уровень сложности, чтобы действительно над чем-то задуматься (например, приевшийся калькулятор). Они не показывали, что объекты могли бы решить многие проблемы.
В программе полно таких неявных объектов - служебных объектов: считают, фильтруют, агрегируют. Никогда не задумывался над тем, что практически любой for можно (наверное, даже лучше) заменить на объект, инкапсулирующий необходимую логику.
Пожалуй единственное, что меня ограничивало - идефикс, того, что объекты должны представлять концепции реального мира. Кто мне вообще это сказал?
Диаграммы мешают в понимании ООП
Но что насчет популярных инструментов проектирования? Нотаций. Наверное все видели различные UML диаграммы. Диаграмму классов так наверное любой программист должен был видеть хоть раз.
ER диаграммы тоже хороши - они слишком сильно сцеплены с реальным миром. Там почти все представляет объекты реального мира.
Поразмыслив, я понял 3 вещи:
ER диаграмма ничего не имеет общего с ООП - это инструмент для бизнес-анализа. Я не обязан создавать такие же классы, как и на этой диаграмме. Кто мне такое сказал?
UML показывает высокоуровневую структуру программы: кто в ней есть и что они должны делать/иметь. Т.е. что делать, а не как делать. Реализация ложится на плечи программиста (спойлер, это будут методы на 100+ строк из циклов, условий и других прелестей)
Многие нотации ориентированы для простого понимания концепций программы - из каких компонентов состоит. Ничто не мешает нам вместо классов передавать массивы 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;
}
}
Как же стало удобно! Все находится рядом, сигнатуры стали короче и поддержка автодополнения в подарок - прелесть!
Выводы
Что я вынес из всего этого?
-
Объект это не концепция реального мира. Можно сделать объект который имеет имя, атрибуты, поведение, как у объекта реального мира, сделать максимально похожим, но это НЕ ОБЪЕКТ РЕАЛЬНОГО МИРА. Надо прекратить думать в данном ключе!
Объект - это (всего лишь) данные и функции, ассоциированные с ними
На каждый блок с логикой (цикл, последовательность условий и т.д.) я смотрю с мыслью: “Нельзя ли вынести это в отдельный объект?”
Таким же образом, смотрю на функции, которые принимают одинаковые аргументы. Их всех можно объединить в объекты, атрибутами которых являются эти общие аргументы.
P.S. Я не радикал, а за осмысленное и прагматичное использование объектов: для тривиальной логики можно оставить циклы, разрешаю)
Комментарии (74)
Zara6502
15.09.2022 08:45+1Хм, даже не знаю что сказать. Задолго до появления ООП в моей жизни я занимался структурным программированием, потом базами данных и как-то не возникало идеи данные внутри ПК ассоциировать с реальным миром. Позже, уже к концу 90-х столкнулся с ООП и я не увидел никакой необходимости что-то менять в понимании кода и структур данных и как-то их связывать в реальностью. По сути тока соприкосновения одна - и данные реального мира и данные в ПК - это закодированные данные, а система кодирования и раскодирования может быть весьма причудливая.
Конкретно по вашей статье - я не люблю ООП, я не чувствую оформленную таким образом программу структурно целой, для меня это куча разрозненных кусков кода который мечется от объекта к объекту. Но это чисто моя проблема. Но манипуляции с объектами, когда вы объединили три в один, на мой взгляд ничего не меняют абсолютно кроме стилистики оформления, ну и заставят вас писать более длинные названия методов.
Вообще странно что сама идея всё видеть в виде объектов пришла вам задолго после того как вы познакомились с ООП, я так мыслю с 1986 года когда сел программировать на Бейсике. Мир сразу стал другим.
IvanSTV
15.09.2022 12:10-1Но манипуляции с объектами, когда вы объединили три в один, на мой взгляд ничего не меняют абсолютно кроме стилистики оформления, ну и заставят вас писать более длинные названия методов.
ИМХО, объекты упрощают понимание, но только при ассоциации с объектами внешнего мира. Например, когда я создаю объект "Pallet", то образно ассоциирую его с паллетом на складе, и дальше у нас идут свойства и методы - вес, объем, состав, сборный или целый, у него там есть заказчик, он позволяет поересчитать количество коробок на нем, запаллетировать\распаллетировать в системе. И это сильно упрощает работу- когда мне говорят, что еще надо учитывать, ADR паллет или обычный, то я добавляю свойство к объекту, а не вставляю куски кода в различные места, редактируя с ошибками индексы и переменные. Это понятно, потому что идет моделирование реального мира.
А создать объект ради объекта - это какой-то мазохизм или я что-то не понимаю в жизни.
Zara6502
15.09.2022 13:52-1А как быть мне если я почти никогда не пишу код хоть как-то ассоциируемый с реальным миром? Ваши паллеты - это 1% темы. Да, удобно, но и очевидно, не вижу тут противоречий с моими словами. Это называется - прикладное применение.
Один из примеров, в 1992 году я делал курсовую на Clipper+DBF, за тему взял статью в журнале - кем я был в прошлой жизни. Там было 12 знаков зодиака, какие-то таблички с рунами, много всего. В итоге я сделал штук 7 таблиц, где происходила связь по ID, таблички были в виде ID, RUNE_ID, ZODIAK_ID. Связь с реальным миром была потеряна, кто-то не имеющий статьи из журнала не сможет восстановить информацию, так как будет только набор чисел.
Или когда пишешь игру на ассемблере - какая там связь с реальностью? Всё выдумка, а объектов сотни. Просто ваша работа связана с конкретными вещами, это как 1С-ники - у них деньги, товар и документы, других объектов нет.
IvanSTV
15.09.2022 14:15про игру некорректно - игра моделирует реальность в первом приближении. Никто не может придумать ничего, чего бы не было в реальной жизни так или иначе. Свойства и взаимосвязи берутся так или иначе из пыта реального мира. Абсолютных абстракций не существует, разве только у шизофреников
Zara6502
16.09.2022 07:37Как много реальных Богов вы знаете? Как много антропоморфных инсектоидов? Или то что у Бога на изображениях лицо человека, то вы считаете что это модель реальности?
Математика - это способ описать и понять мир и с реальностью оно вообще никак не связано, это абсолютная абстракция придуманная человеком. Именно так человек и программирует - создаёт абстракции, которые описывают другие абстракции, на выходе - да, это может быть информация применимая в реальности, а может и не быть.
Если так не понятно - вот пример - изображение. Фотоны попадают в глаза, регистрируются и в виде импульсов (закодированная информация) попадают в мозг, где декодируется и формируется изображение, которое отчасти соответствует реальности. Если рядом с глазами вы поставите камеру, то для регистрации фотонов у вас будет или пластина (пленка) которая будет менять химически молекулы (кодировать), а потом вы сможете изображение восстановить. Оба способа - импульсы и химия - это способы кодирования в абстрактную сущность - изображение. Почему она абстрактная - потому что есть убрать камеру и человека то объекты, отражающие или испускающие фотоны не перестанут этого делать (вопрос наличия наблюдателя и его влияния на систему мы обсуждать надеюсь не станем).
Для обработки изображения я создам класс Picture в ней объект Raw и методы getPixel, setPixel например - что из этого хоть какое-то отношение имеет к реальности? Это всё абстракции в попытке моделировать объекты из реальности. Pixel - это не объект реальности, как и матрица с сырыми данными Raw.
Finesse
15.09.2022 08:47+2То, что вы открыли для себя, называется декомпозиция. ООП (и объекты в частности) — это один из способов реализации декомпозиции. Этот способ является лучшим далеко не всегда, но это зависит от языка программирования. ООП можно использовать не только для моделирования реального мира, применение ООП ограничено только фантазией программиста.
Часто код будет намного понятнее, если сделать просто функцию вместо класса. Класс со статическими методами — ок, когда в языке не удобно работать с простыми функциями. Объект с обычными методами имеет смысл, когда он хранит внутри себя некоторое состояние или зависимости.
Videoman
15.09.2022 14:53+1Полностью согласен, но хотел бы еще более конкретизировать:
1. Если требуется несколько сложное состояние, инвариант, в этом случае требуется объект, который и будет поддерживать это состояние через внешний интерфейс, в строгой согласованности.
2. Также объект может быть полезен, если некие данные абсолютно независимы друг от друга, могут меняться не согласованно, но по логике очень часто передаются вместе. Частный случай — структура.
Во всех остальных случаях функции предпочтительнее и гибче, на мой взгляд.
vitalynec
15.09.2022 18:15применение ООП ограничено только фантазией программиста
Это вроде называется абстракция, принцип ООП, о котором почему-то постоянно забывают.
s_f1
15.09.2022 08:55-1Ваш мир никогда не будет прежним:
In Python, everything is an object.GospodinKolhoznik
15.09.2022 09:43-1Я питонист конечно не настоящий, но не раз слышал, что In python everything is an object.
Запускаю repl, что же я там вижу:
>>> class File "<stdin>", line 1 class ^
Чьёрт побьерьи! Я ожидал увидеть что-то вроде:
>>> class <class 'class.Class'>
Та же фигня и для if def return = \n \. \, ну и много ещё для чего.
fireSparrow
15.09.2022 11:24+3То что вы написали, не является законченной синтаксической конструкцией, оно само по себе вообще никак не парсится в ast. Соответственно, некорректно пенять, что оно не является в питоне объектом, т.к. оно в питоне вообще ничем не является.
То есть если простыми словами — то, что вы вы написали в вашем примере, до самого питона вообще даже не дошло.
Это как если бы мы сказали, что в русском языке всё является одной из десяти частей речи, а кто-нибудь привёл бы в качестве контрпримера "!№;%:?*()", заявив, что раз оно не является ни одной из 10 частей речи, то наше утверждение не верно.GospodinKolhoznik
15.09.2022 13:16Тогда может неправильно говорить, что все сущности языка являются объектами, если существуют такие сущности, которые не то, что не объекты, а даже в аст не парсятся?
fireSparrow
15.09.2022 13:44если существуют такие сущности
Вот только это и не сущность языка.
Если что-то не является корректной и законченной синтаксической конструкцией, то вообще нельзя сказать, что с точки зрения языка оно чем-то является или не является.
Так-то действительно существует куча вещей, которые не являются объектами питона. Например, галактика Млечный Путь, или запах розы, или роса на траве. Они не объекты питона, но это только потому, что они вообще лежат за пределами питона. Ровно так же, как и некорректные синтаксические конструкции.GospodinKolhoznik
15.09.2022 14:41-1Зарезервированные слова языка не являются его сущностями? Ну извините.
Операция плюс '+' сама по себе тоже не является законченной грамматической конструкцией, и сама по себе правильно для аст не распарсится. Однако же объектом является, и ее можно передать как аргумент функции, например. А зарезервированное слово class не является объектом, и как аргумент функции его не передашь, а ведь можно было бы и передать. Например, передавая зарезервированные слова как аргументы можно было бы их как то конкатенировать друг с другом и получать уже законченные конструкции. Ан нет!
Ведь если уж кто то заявляет, что всё есть объект, то уж хотя бы зарезервированные слова должны быть объектами.
fireSparrow
15.09.2022 15:01Вот зачем люди спорят о вещах, в которых вообще ничего не понимают?
Однако же объектом является, и ее можно передать как аргумент функции, например.
Нет, плюс не является объектом, и нет, его нельзя передать в функцию. Вы бы хоть проверяли те фантазии, которые приходят вам в голову, прежде чем высказать их как факт.Ведь если уж кто то заявляет, что всё есть объект, то уж хотя бы зарезервированные слова должны быть объектами.
С чего бы это вдруг? Вы вообще понимаете разницу между элементами синтаксиса языка и сущностями рантайма?GospodinKolhoznik
15.09.2022 15:15-1Давайте ещё раз. Утверждение "в питоне всё есть объект". Контрпример - зарезервированное слово class. А это другое надо понимать, class не является законченной грамматической конструкцией и в аст не парсится.
Вопрос: если все есть объект, то почему class не является законченной грамматической конструкцией?
Как я вижу он вполне себе мог бы быть законченной грамматической конструкцией и объектом - таким объектом у которого мог бы быть метод concat(), чтобы объединив его с другими объектами получить другую грамматическую конструкцию - аналогично частично применненной функции. Но он таковым не является. Значит всё, да не не всё.
З.Ы. Я сразу написал, что питонист не настоящий. И каюсь, я был уверен, что + это точно объект. Ну если нет, то это вообще цирк. Одна из самых часто используемых функций не является объектом. Рука-лицо!
fireSparrow
15.09.2022 15:32Давайте ещё раз. Утверждение «в питоне всё есть объект». Контрпример — зарезервированное слово class.
С чего бы это является контрпримером? Если вы пишите просто «class», то это вообще не является корректной программой на питоне. У вас будет просто ошибка синтаксиса.
Как, чёрт возьми, контрпримером о питоне может являться то, что не является сущностью питона?GospodinKolhoznik
15.09.2022 16:18Я из утверждения "все является объектом" делаю однозначный вывод, что зарезервированные слова тоже являются объектом. И что функция языка питон + тоже является объектом. Ну по крайней мере по логике вещей так должно быть. Но у питона своя особакя логика.
fireSparrow
15.09.2022 16:43Я из утверждения «все является объектом» делаю однозначный вывод, что зарезервированные слова тоже являются объектом.
Это исключительно ваша неверная трактовка. Элементы синтаксиса и сущности рантайма — это вообще разные вещи. Эдак можно требовать, чтобы клавиатура тоже являлась объектом — мы же питоновский код на клавиатуре набираем!И что функция языка питон + тоже является объектом.
Символ плюса — это не функция.Но у питона своя особакя логика.
Вы знаете много других языков, где элементы синтаксиса и сущности рантайма являются одним и тем же?
GospodinKolhoznik
15.09.2022 17:15-1Пустой разговор.
Я прицепился к словам, думал будет забавно, получилось грустно.
geher
15.09.2022 17:35Вы знаете много других языков, где элементы синтаксиса и сущности рантайма являются одним и тем же?
Проблема в том, что фраза "в питоне все есть объект" ормально скорее про элементы его синтаксиса, чем про сущности рантайма, ибо элементы синтаксиса таки в языке, а не где-то еще, а вот сущности рантайма уже не совсем в языке. И если кто-то утверждает, что в питоне все есть объект, то это формально однозначно означает, что элементы синтаксиса являются объектами.
Другой вопрос, что люди любят формально некорректные по смыслу высказывания для обозначения вполне корректных по смыслу вещей. Например, "в UNIX все есть файл", хотя там можно найти много всякого, что просто не может быть файлом.
А потому дискуссия ваша выглядит несколько забавно, но не более того.
perevedko
15.09.2022 12:11+1Переменная — не объект. Не то, на что она указывает, а сама переменная. if then else и прочие конструкции — тоже не объект.
ksbes
15.09.2022 09:30+5Автор (и, как я смотрю по комментариям — не только автор) как-то путает понятия. А именно — объект и класс. Это разные понятия, даже немного из разных областей. Вполне себе есть языки с объектами без классов (Javascript ) и можно представить экзотику(есть такие?) ввиде классов без объектов (с натяжкой можно отнести типизированные функциональные, вроде Haskel).
Объект — это всё же программисткая концепция, развитие идей структурных типов.
Класс — это вообще из теории множеств, там всякие брадобреи и прочее (да, это программисткий термин восходит именно к математическому, так же как и лямбда-функции).
«Классическое» ООП, которое преподают на этом различии особо не заморачиваются, т.к. мейнстримовсие языки более-менее жалеют мозги программистов и прячут весь тот математический трип, на котором они построены за красивыми языковыми конструкциями.
Ну и да, непонимание тонкостей и истории понятий (ООП 60-х — это совсем не ООП сегодня) приводит к неумению грамотно пользоваться инструментом. Вроде с этим должны справляться наборы принципов вроде SOLID — но их тоже понимают в лучшем случае по-разному (если вообще хотят понимать). Вот и получаем, что на ООП-языках вроде JAVA, C#, Python люди пишут «на бейсике». Это можно поставить в вину языкам — что позволяют такое. Но всё же на любой Тьюринг-полной системе можно писать «на бейсике» (а можно и организовать ООП).
Это ни плохо, потому что программист — он инженер, а не учёный — ему вся эта матмуть справедливо до лампочики. Ему набор готовых велосипедов и техник езды на них нужен.
Но и не хорошо, т.к. погроммист скотина думающая (это я про себя) и начинает из этих велосипедов строить свою «мета-велосипедную» науку, накручивая сложности там где не надо.4reddy
15.09.2022 13:52https://wiki.c2.com/?AlanKaysDefinitionOfObjectOriented
И там дальше по ссылкам
dididididi
15.09.2022 09:51+5Вы же не прикалываетесь? Все вот эти сервисы, передавамые как аргумент функции? Это же треш! Это сервис типа стейтфул объект? Почему в статический класс передаются экземплры сервисов? Что это ваще?
RH215
15.09.2022 09:59Что это ваще?
Пример как делать не надо ниже которого пример как сделать лучше.
dididididi
15.09.2022 10:30+1ТАм оба примера, как не надо делать никогда. Второй на мой взгляд хуже, но это вкусовщинка же.
vadimr
15.09.2022 10:29+7// Количество рабочих часов на каждый день недели 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; } }
Жёстко.
s_f1
15.09.2022 11:39+2Недостаточно жёстко. Нужно было так:
// Количество рабочих часов на каждый день public class UserSchedule { public float FirstOfJanuary{ get; set; } public float SecondOfJanuary { get; set; } ... public float ThirtyFirstOfDecember{ get; set; } }
Glays
15.09.2022 12:40Я надеюсь это просто учебный пример и в реальности автор использует производственный календарь
ksbes
16.09.2022 09:04Я надеюсь это просто учебный пример и в реальности автор использует производственный календарь
Без шуток — я знаю (работал на) организацию, которая до сих пор использует в своём закупленном пропроитарном софте (от компании с мировым именем) рабочий календарь, который именно так и устроен. Буквально именно такая структура по дням недели.
Причём каждый год они «вручную» (естественно, я скрипт написал который они запускали) дозаписывают в БД на каждый день следующего года строчку в БД. И если немного провафлить (что было пару раз) — то у людей после праздников ничего не будет работать.
И я не могу сказать что это не оправданно — в той архитектуре такая схема была очень органичной.
Хотя да, когда с таким работаешь — просто чуствуешь, как в тебе отключается всё человеческое и ты просто исполняешь приказы. Ja, ja.
warlock66613
15.09.2022 10:51Вы движетесь в правильном направлении, но впереди у вас ещё долгий путь. Хотя в целом вы видимо освоили ООП и теперь можно уже начинать потихоньку учиться отказу от ООП.
manyakRus
15.09.2022 11:20У вас какой-то ограниченный язык программирования что всё приходится пихать в объекты (java).
В нормальном языке есть ещё модули, которые никак не связаны с объектами (golang)
Похожие функции надо было объединить в один модуль а не в один объект - фигнёй занимаетесь и других учите неправильно :-(
SadOcean
15.09.2022 11:22+1Ну частично не соглашусь с основным выводом.
Конечно объект - не обязательно сущность реального мира.
Технически это действительно набор полей и методов их обработки.
Но хороший объект - это все же хорошая абстракция.
Не стоит лепить все оставшиеся бесхозными данные и операции в отдельный класс только потому, что они в объекты плохо вписываются.
Поэтому нет проблем в объекте, который содержит данные процесса (то есть не сущности, а действия). Собственно есть целый паттерн "команда". И целая парадигма Data Context Interaction, направленная на представление операций в объектном стиле.И, точно так же, многие вспомогательные объекты упрощают работу основных объектов, как виртуальные частицы в физике - их самих не существует, но они обеспечивают некоторое взаимодействие, зная про все объекты, участвующие в них.
evgenyk
15.09.2022 11:48Мне кажется автор очень верно поставил вопрос. В учебниках по ООП именно так и обучают программированию, учать классифицировать объекты реального мира, искать общие и уникальные свойства, на основе такой классификации разрабатывать иерархию классов, потом генерить на основе этих классов объекты и программа будет работать как множество этих объектов, взаимодействующих между собой. Иногда такой подход даже работает.
Но, ИМХО, НАСТОЯЩАЯ ПРАВДА в том, что есть разные типы задач. И для разных типов задач хорошо использовать разные архитектурные подходы. Соответствующий типу задачи подход позволяет упростить внутреннюю структуру программы, ее написание, отладку и модиффикащию.
Давайте разберем парочку примеров для конкретности.
1) Задача состоит в работе большого количества похожих сущностей, имеющих много общих свойств. Например графический интерфейс, или компьютерная игра. В таком случае более менее подходит описанный в первом абзаце традиционный подход с иерархией классов и объектами на основе этих классов.
2) Задача состоит из одного или нескольких больших массивов обрабатываемых данных и операций над ними. В таком случае возможно лучше подойдет просто испольование глобальных массивов данных и набора работающих с ними функций.
3) Случай 2) но с более сложными функциями обработки данных, с состояниями и т.д. Тут могут подойти объекты имеющие внутреннее состояние и работающие с данными. Но городить иерархию классов тут как правило не нужно.4) Чисто вычисления на базе входных данных без внутренних состояний. Тут может подохти чисто функциональный подход.
5) И так далее...
Лично мой любимый метод использования классов в питоне - рассматривать и использовать класс и созданный на его базе объект как state machine. В простейшем случае два состояния, до инициализации и после инициализации.
Иногда питоновские классы я вообще использую просто как пространство имен.
Проблема ИМХО в том, что в книгах как правило проповедуется один любимый поход для всех классов задач.
Что касается одинаковой логики, используемой в разнородных классов, то мне кажется, что такую логику лучше выносить в отдельные функции.
geher
15.09.2022 13:02+2Объект - это (всего лишь) данные и функции, ассоциированные с ними
Совершенно не так.
Объект - это осмысленно соединенные функции и данные. Объект в программе, естественно, не обязан отражать объект из реального мира (почему-то у меня и мысли такой не возникло, хотя учили, как и многих, на стандартных примерах про животных). В его основе может лежать любая абстракция. Но это не должно быть произвольным сочетанием функций и данных, которые связаны только тем, что в программе случайно оказались рядом.
sophist
15.09.2022 13:57Объект это не концепция реального мира
Объект это не концепция реального мира, это концепция модели реального мира.
iig
15.09.2022 14:00Если, например, вы используете реляционную базу данных - поздравляю, у вас ООП. Структура таблицы - класс; строка таблицы - экземпляр класса.
ksbes
15.09.2022 14:14+1Нет. Это так не работает. Реляционная модель и модель ООП очень плохо дружат — это головная боль всех ORM-ов. (в БД нет наследования, в ООП — полноценных FK).
Объект — это в первую очередь его поведение. Которое да, зависит от внутреннего состояния объекта. Но в идеале об этом состоянии извне никто не должен знать и никого оно не должно ни коим образом интересовать.
А строка в БД — это именно состояние. Это структура. А не объект.
lair
15.09.2022 15:24+1На каждый блок с логикой (цикл, последовательность условий и т.д.) я смотрю с мыслью: “Нельзя ли вынести это в отдельный объект?”
А зачем? Почему нельзя вынести эту логику в отдельный метод?
nikolas78
16.09.2022 22:43Наверное из-за устаревшего понимания ООП как «все есть объект», вместо разделения на структуры данных и внешние методы.
lair
15.09.2022 16:19Никогда не задумывался над тем, что практически любой for можно (наверное, даже лучше) заменить на объект, инкапсулирующий необходимую логику.
Зачем заменять
for
на объект? И как вы это сделаете?
beeptec
15.09.2022 22:03-1Тот кто комфортно разрабатывает посредством ООП ни за какие пряники не вернется к фундаментальным основам языковой парадигмы, на которой это ядро строилось, хотя бы по той причине что логическое ядро на котором построен ООП оптимизировано готовыми модулями, которые есть хороший и правильный код, многократно откатаны и не нуждаются в их дизассемблеризации, больше скажу, такие попытки чреваты превращением логического ядра в непредсказуемый бесполезный хлам и развалом всех зависимостей.
Что касается диалога между разрабами, ООП для того и создавался, чтоб не выносить в коллективных спорах друг другу мозги терминологией, которая по той или иной причине воспринимается субъективно и зависит от всего что было предтечей такого мышления.
В ООП всегда представлена упрощенная интерфейсная абстракция максимально приближенная упрощенному, человеческому восприятию.ООП прежде всего интересен в обсуждениях тогда, когда происходит демонстрация абстракции интерфейса, приведением сравнительных примеров программирования и того, ценой какого времени в т.ч. и скилов тот или иной метод позволяет разрабу представить клиенту желанную "Улыбку Джоконды"...
lair
15.09.2022 22:52Тот кто комфортно разрабатывает посредством ООП ни за какие пряники не вернется к фундаментальным основам языковой парадигмы, на которой это ядро строилось
Какое ядро? К каким "фундаментальным основам"? Ничего не понятно.
логическое ядро на котором построен ООП
Это какое "логическое ядро"?
ООП для того и создавался, чтоб не выносить в коллективных спорах друг другу мозги терминологией, которая по той или иной причине воспринимается субъективно
Неа, ООП не создавался для этого. И вокруг ООП до сих пор постоянно спорят о терминологии (ну вот например — что такое инкапсуляция?).
В ООП всегда представлена упрощенная интерфейсная абстракция максимально приближенная упрощенному, человеческому восприятию.
Это утверждение нуждается в доказательстве.
beeptec
16.09.2022 09:20-1Помигать ледом 10 раз на ассембле (архитектура STM32):
Код:@GNU AS @ Настройки компилятора .syntax unified @ тип синтаксиса .thumb @ тип используемых инструкций Thumb .cpu cortex-m3 @ микроконтроллер .include "STM32F100.INC" @ файл определений микроконтроллера @ макрос псевдокоманды MOV32, пока просто используем, не вникая как, что и почему .macro MOV32 regnum,number MOVW \regnum,:lower16:\number MOVT \regnum,:upper16:\number .endm @ таблица векторов прерываний .section .text .word 0x20010000 @ Вершина стека .word Riset+1 @ Вектор сброса Riset: mov r0,r0 mov r0,r0 mov r0,r0 @ включим тактирование GPIOH MOV32 R0, RCC_APB2ENR @ адрес MOV32 R1, 0x04 @ значение LDR R2, [R0] @ прочитали значение регистра ORR R1, R1, R2 @ логическое, побитовое ИЛИ: R1= R1 ИЛИ R2 STR R1, [R0] @ запись R1 по адресу указанному в R0 @ установим режим GPIOH PH2 MOV32 R0, GPIOA_CRL @ адрес MOV32 R1, 0x22222222@0x2000 @ значение LDR R2, [R0] @ прочитали значение регистра ORR R1, R1, R2 @ логическое, побитовое ИЛИ: R1= R1 ИЛИ R2 STR R1, [R0] @ запись R1 по адресу указанному в R0 BLINK_LOOP: @ включим светодиод MOV32 R0, GPIOA_BSRR @ адрес MOV32 R1, 0x08 @ значение STR R1, [R0] @ запись R1 по адресу указанному в R0 MOV32 R0, GPIOA_ODR LDR R2, [R0] @BL DELAY @ пауза @ выключим светодиод @MOV32 R0, GPIOA_BSRR @ адрес @MOV32 R1, 0x08 << (1*16) @ значение 1-размер поля, 16-во второе полуслово @STR R1, [R0] @ запись R1 по адресу указанному в R0 @BL DELAY @ пауза B BLINK_LOOP @ делаем цикл DELAY: MOV32 R2, 0x00100000 @ повтор цикла задержки 0x0010 0000 раз. Delay_loop: SUBS R2, R2, 1 BNE Delay_loop BX LR .end
Аналогично, вместо кода функциональные инструкции декларативно в абстрактном построенном на ООП интерфейсе (архитектура х86):
Вот и рассуждайте теперь, как с каланчи классического кодера, по ходу пораскинуть здесь ветки, устроить кострище и увиденное придать анафеме.
И естественно хаброминус ;)lair
16.09.2022 14:03А какое отношение ваша картинка имеет к моим вопросам?
PS Помигать ледом 10 раз в ООП:
for _ in times(10): led.blink()
beeptec
16.09.2022 14:11Картинка отвечает на все Ваши вопросы. А вот Ваш код не рабочий.
Если не согласны, то покажите Ваш код в Работе.
Я да, могу, и в отличие от Вас свои коменты НЕ подкрепляю минусами, в понимании, себе лучезарному, что карма над головой сверкает всеми цветами радуги.
Во всех моих публикациях Ваши минусы, самые минусатые.
Дальше не отвечаю. Оставайтесь при своем мнении.lair
16.09.2022 14:14Картинка отвечает на все Ваши вопросы.
Нет.
А вот Ваш код не рабочий.
Вы, я так понимаю, никогда не слышали про псевдокод? От того, что он псевдокод, он не перестает обладать признаками ООП.
beeptec
16.09.2022 20:23Вы, я так понимаю, никогда не слышали про псевдокод? От того, что он псевдокод, он не перестает обладать признаками ООП.
А что я Вам продемонстрировал? Как и то что у Вас, все это то, что предназначено для представления алгоритма человеку для понимания, а не для компьютерной трансляции и последующего исполнения программы. В моем случае т.н. псевдокод есть не просто алгоритм, а функциональная инструкция в связке с ООП.lair
16.09.2022 20:25А что я Вам продемонстрировал?
Какую-то картинку.
функциональная инструкция в связке с ООП.
… я, собственно, не понимаю, где в вашей картинке ООП.
(честно признаюсь, про "функциональные инструкции" применительно к программированию я от вас первый раз услышал)
beeptec
16.09.2022 20:50Как я догадываюсь Real Time software embedding, не Ваше. По части применения ООП в разработке фреймворков в качестве среды построения функциональных инструкций для внешнего аппаратного управлением, Вы так же не при делах.
Кроме того, как я уже ранее и неоднократно отвечал, что в дискуссиях о применении декларативных методов программирования, с применением ООП, для понимания метода конечным юзерам важно представить абстракцию интерфейса именно в работе, дальше как хотите, так и трактуйте свое понимание.
Выставляю про LED мигание, не столько для Вас, сколько для тех, кого это реально может заинтриговать:lair
16.09.2022 20:55По части применения ООП в разработке фреймворков в качестве среды построения функциональных инструкций для внешнего аппаратного управлением, Вы так же не при делах.
Не, "не при делах". Я все больше по LOB-приложениям.
для понимания метода конечным юзерам важно представить абстракцию интерфейса именно в работе
Эм. Вы про программный интерфейс или про пользовательский интерфейс? Кто у вас "конечный пользователь"?
beeptec
16.09.2022 21:14Эм. Вы про программный интерфейс или про пользовательский интерфейс? Кто у вас "конечный пользователь"?
В контексте в.с. речь идет об автономном программировании, соответственно я говорю здесь исключительно о создании абстракции пользовательского интерфейса для декларативного программирования, прототайпинга и разработки конечного исполнительного кода.
Пользователи, они же разработчики средств автоматики, робототехники и смарт систем. Должны обладать определенными знаниями в аппаратной бинарной логике, без знаний языков программирования.
lair
16.09.2022 21:16В контексте в.с.
В контексте чего?
создании абстракции пользовательского интерфейса
Я не понимаю, что такое "абстрация пользовательского интерфейса". Точнее, я могу придумать слишком много вариантов трактовки этой фразы, так что лучше объясните, что вы имеете в виду.
пользовательского интерфейса для декларативного программирования, прототайпинга и разработки конечного исполнительного кода. Пользователи [...] Должны обладать определенными знаниями в аппаратной бинарной логике, без знаний языков программирования.
Гм. Тогда при чем тут вообще ООП?
beeptec
16.09.2022 21:57-1Гм. Тогда при чем тут вообще ООП?
Императивные и декларативные стили программирования не являются взаимоисключающими, при этом писать программы только в одной из указанных парадигм подталкивают правила синтаксиса, когда в одном случае программист ограничен набором синтаксических правил, а в другом набором функций, предоставленных интерфейсной абстракцией. Важно понимать, что парадигма декларативного стиля, как правило имеет целевое значение под конкретную область. К примеру в представленной здесь платформе разрабу предложен настраиваемый фреймворк состоящий из набор FSM как статических так и динамических.
Замечу, я не владею в полной степени ни одним из императивных языков, что не помешало мне все же построить свою декларативную абстракцию с интерфейсом и коммуникациями I/O управления двоичной логикой на аппаратном уровне.В языках низкого уровня, например в ассемблерах, состояниями могут являться память, регистры и флаги. Инструкциями же выступают команды, которые поддерживаются целевым процессором.
В языках более высокого уровня, например в Си, состоянием является исключительно память. Инструкции в таком случае могут быть более сложными, а также приводить к выделению и высвобождению памяти по мере своего функционирования.
В моем случае, посредством высокоуровневого ООП изначально были разработаны статические и динамические конечные автоматы с коммуникациями по протоколам бинарной логики, фреймворк с клиентским интерфейсом, в Вашей терминологии - менеджер, он же настраиваемый конфигуратор состояний этих автоматов и сценариями обращений, со стороны это выглядит свободно программируемым виртуальным контролером, который функционально ограничен набором настраиваемых возможных состояний в.у. автоматов.
Если вы досмотрели представленный здесь видеоролик до конца, там наглядно продемонстрирована декларативная методика программирования.
lair
16.09.2022 22:01В моем случае, посредством высокоуровневого ООП
Я продолжаю не понимать, где у вас на вашей картинке "высокоуровневое ООП".
Если вы досмотрели представленный здесь видеоролик до конца, там наглядно продемонстрирована декларативная методика программирования.
Тем более не понимаю, при чем тут ООП.
beeptec
16.09.2022 23:07В процессе разработки представленной платформы на языке G применялись функции OOП, которые являются основным элементом разработки в LabVIEW.
Больше мне нечего добавить, старался терпеливо объяснять пока не прилетело какое то НЛО.
Мои наилучшие пожелания.
lair
16.09.2022 23:11+1В процессе разработки представленной платформы на языке G применялись функции OOП, которые являются основным элементом разработки в LabVIEW.
Гм. То есть картинка, которую вы показываете, к ООП имеет только то отношение, что где-то при разработке этого софта применялся ООП?
Тогда мои вопросы из первого коммента продолжают быть актуальны, потому что она (картинка) никак на них не отвечает.
lair
16.09.2022 14:59Во всех моих публикациях Ваши минусы, самые минусатые.
Что-то мне любопытно стало, а в каких это ваших публикациях (множественное число!) вы видели мои минусы? Мне казалось, что минусы на хабре анонимны, нет?
beeptec
16.09.2022 20:30Что-то мне любопытно стало, а в каких это ваших публикациях (множественное число!) вы видели мои минусы? Мне казалось, что минусы на хабре анонимны, нет?
Очень жаль что минусаторы в анонимах. Это чудо философия с кармическими отметками, для тех, у кого детство не задалось.
Мы то с Вами не первый день пересекаемся и чего лукавить, именно после Ваших реакций на мои коменты эти самые дела и происходят. Могу успокоить, ответ знаете только Вы.lair
16.09.2022 20:32Мы то с Вами не первый день пересекаемся
Да ну? Я что-то не помню, чтобы я отвечал на ваши комментарии кроме как в этом посте. И уж точно не комментировал вашу единственную публикацию.
michael_v89
16.09.2022 09:00Объекты должны представлять концепции реального мира
Это верное утверждение, но надо немного шире трактовать понятие "реальность". Это реальность не для нас, а для программы. Всё, что находится вне программы, это реальность. Внутри программы есть информационная модель этой реальности. То есть правильнее говорить не "реального мира", а "внешнего мира". Модель в программу закладывает программист. Он строит модель при анализе предметной области.
Предметная область это необязательно физические объекты, это может быть например сценарий игры. Для программы это все равно внешняя по отношению к ней сущность. Чтобы программа вела себя в соответствии с задуманным авторами игры сценарием, программист должен заложить в нее соответствующую информационную модель этого сценария.
Правильная модель это такая, которая максимально точно моделирует поведение моделируемой сущности и составляющих ее элементов. Поэтому да, объекты в программе должны представлять концепции внешнего для программы мира.
Это не в программе есть какие-то дополнительные объекты, а в предметной области есть объекты, которые мы не замечаем. Оператор, который заполняет заявку по данным клиента, действует по инструкции заполнения этих заявок, которую ему сообщили при обучении. Эту же инструкцию сообщили другим операторам. В данном случае оператор это просто процессор, выполняющий инструкцию и запоминающий какие-то временные значения. Поэтому вынесение этой логики в отдельный объект с состоянием и поведением это правильная модель этого процесса.
Отдельные элементы программы это тоже информационные системы, и к ним можно применить тот же принцип. Сущности "Схема базы данных" нет в предметной области, но она есть в другой информационной системе "база данных", с которой взаимодействует наша программа.
Из этого идет и разделение программы на слои. В слое бизнес-логики содержится модель предметной области, в других слоях соответствующие им модели внешних систем или правил. Например, понятие "DI-контейнер" не относится к бизнес-логике, это модель договоренности "Давайте у нас будет специальный инструмент, который будет пробрасывать зависимости в объекты в соответствии с нашими настройками".
panzerfaust
А кто или что вообще заставляло вас думать в этом ключе? Это в манифесте какого-то языка заложено? Или у какого-то автора типа Шилдта постулировано?
По мне так это такой детский миф, живущий в джуновской среде. Только на джуновских собеседованиях я слышу про все эти "объекты реального мира", "наследование для переиспользования кода" и "Cat extends Animal". На практике принципиально другие вопросы задаются: "это работает?", "это тестируемо?", "это расширяемо?", "лучшим практикам не противоречит?" "это не антипаттерн?", "через месяц пойму, что написал?". Никто уже всерьез не упарывается экзистенциальным поиском святого Грааля ООП. Тем более, что почти все популярные языки давно уже мультипарадигменные.
12rbah
Так вроде это используют, чтобы объяснить концепцию, человеку, который не знаком с ООП проще понять, что-то на реальных примерах, правда проблема в том, что ряд людей дальше понимания "Cat extends Animal" не заходят.