Ещё важное дополнение про проектирование с ООП, и немного по экстремальное программирование

МЕНЮ


Главная страница
Поиск
Регистрация на сайте
Помощь проекту
Архив новостей

ТЕМЫ


Новости ИИРазработка ИИВнедрение ИИРабота разума и сознаниеМодель мозгаРобототехника, БПЛАТрансгуманизмОбработка текстаТеория эволюцииДополненная реальностьЖелезоКиберугрозыНаучный мирИТ индустрияРазработка ПОТеория информацииМатематикаЦифровая экономика

Авторизация



RSS


RSS новости


2022-09-21 06:09

Ещё важное дополнение про проектирование с ООП, и немного по экстремальное программирование. Есть хорошая книга "Объектно-ориентированное мышление" Мэтта Вайсфельда, переведённая к счастью на русский. Она довольно необычная, потому что рассказывает в основном о философии ООП, а не о технических аспектах наподобие инъекции зависимостей. Автор заявляет, что объектное мышление — это революционный прорыв по отношению ко всей предыдущей устоявшейся практике программной инженерии, хотя и критикует взгляд на классы/объекты как на абстрактные типы данных — дескать, это попытка принизить грандиозное новое и сделать его версией старых подходов.

Самое ценное в этой книге, на мой взгляд — это серьёзное отношение к идее моделирования предметной области (домена), которым (моделированием) сегодня практически нигде не занимаются как отдельной сознательной фазой проектирования.

Почему? Ну потому что вот так людей учат.

=

В классических книгах по ООП этой темой вообще ограничиваются в объёме одной главы, где говорится что-то вроде "возьмите описание вашей задачи, выделите существительные в описании — это и есть ваши кандидаты в объекты". Тут засада, что "существительные" из ТЗ слишком провоцируют на выделение классов, которые на самом деле совершенно не нужны. В результате будут появляться лишние классы и пропускаться нужные (подробнее разбираю эту тему на курсах по ООАП).

В результате понимание ООП до архитектурного уровня обычно ограничивается примерно вот такой кривой "прикладной" цепочкой изучения:

- SOLID с акцентом на Open-Closed Principle

- написание тестов для ООП-кода

- паттерны GoF

- TDD для инфраструктуры

https://www.youtube.com/watch?v=AAcPuYfVt2c

+ Лондонская и Чикагская школы TDD

https://softwareengineering.stackexchange.com/questions/123627/what-are-the-london-and-chicago-schools-of-tdd

- Гексагональная архитектура и DDD

Безусловно, все эти вещи хороши и очень полезны для изучения (особенно для того, чтобы успешно проходить собеседования :). Однако это всё никоим образом нельзя назвать сутью объектного мышления, сколь бы подробно вы это всё не заучили. Все эти темки относятся к форме, к уровню синтаксиса. Потенциально идея работать с формой, а не с содержанием, очень сильная — но при условии, что под таким подходом в глубине лежит хорошая математика, и вы хотя бы минимально понимаете, почему лучше делать именно так, а не по другому (в СильныхИдеях ровно это и поясняю).

Ну например, вы допустили канонический косяк

http://wiki.c2.com/?LongMethodSmell

Сеньор сделал code review и ткнул сюда:

http://wiki.c2.com/?ExtractMethod

а потом вы по аналогии сделали

http://wiki.c2.com/?ExtractClass

и теперь уверены, что познали, что такое объектная декомпозиция.

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

Да, если вам повезёт, то ваш декомпозированный класс, возможно, окажется чем-то достаточно значимым в контексте домена. Если вам повезёт меньше, то вы просто выделите какой-нибудь скучный фрагмент инфраструктуры, наподобие IndoorSessionInitializer с картинки.

Декомпозиция на основе объектного мышления — это реально сложно, однако 98% программистов излишне самоуверенны и убеждены, что их великий и гениальный от рождения ум обладает уникальной способностью "естественно" мыслить в парадигме объектов.

=

Декомпозиция должна основываться на модели домена, которая в свою очередь должна быть предварительно подробно сформулирована на фазе анализа (в идеале, формализована алгебраически). Тут, опять таки, популярные учебники по ООП обычно предлагают думать о модели домена как о моделировании предметной области, но .... Та итоговая система, которую мы получаем и которая работает хорошо и надёжно, очень часто отнюдь не моделирует напрямую "реальный мир". С точки зрения её архитектуры невозможно ответить на вопрос "что она делает?" — для децентрализованной схемы (а истинное ООП именно об этом) он бессмысленный.

Мы инженеры, поэтому результат проектирования и реализации мы оцениваем так: насколько проста, гибка, масштабируема система, и насколько точно она отвечает проектным спецификациям (которые сами по себе ничуть не менее реальны, нежели "естественно" воспринимаемые сущности реального мира). Методологически корректнее (по Бертрану Мейеру) говорить не о реальном мире, а о внешнем мире — внешнем по отношению к замкнутому внутреннему миру создаваемой программной системы. Программная модель будет операционной: она формирует объективные практические результаты, и может как-то взаимодействовать с окружающим её миром.

Да, но как этого добиться? Довольно многие полезные вещи в программной инженерии, к счастью, ортогональны друг другу :) Кент Бек (автор TDD и XP) предлагал такую простейшую формулу правильного архитектурного дизайна особо независимо от парадигмы разработки (ООП, ФП или что угодно другое):

1. Все тесты проходят успешно.

2. В системе нету дублирующейся логики.

3. Каждый важный проектный замысел понятен программистам.

4. Количество функций и типов/классов и методов минимизировано.

В своих книгах Кент поясняет, что, в частности, просматривайте ваш код по форме, и везде, где вы видите доменную логику, закодированную дважды похожим образом, найдите способ обобщить или абстрагировать её так, чтобы она была запрограммирована только один раз. Буквально одной строкой про ликвидацию дублирования он передаёт стратегическое содержание множества книг по рефакторингу, SOLID и многих других популярных инженерных подходов (без глубинной математики, и поэтому хорошо продающихся), которые предлагают работать с формой кода с целью улучшения его дизайна.

Только 3-й пункт предлагает немного посмотреть на смысл вашего кода и убедиться, что этот смысл очевиден хотя бы на уровне имён переменных, функций, классов, типов. А 1-й и 4-й пункты — это просто защита от кривого рефакторинга, который может нарушить функциональность, или от безумного увлечения идеями объектно-ориентированного дизайна и ухудшения самого дизайна за счёт его чрезмерного усложнения (а тренд усложнять характерен для очень многих программистов всех уровней).

Но, понятно, легко это сказать, да трудно сделать :)

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

=

Подозрение, что всё то, что я нахожу наиболее интересным и полезным, очень далеко от того, что находит интересным и полезным мир программирования в целом (потому что там рулит бабло :). В начале 2000-х Кент Бек выпустил целую серию книг по XP, которые назывались "Цветные книжки" — у всех был похожий стиль оформления, но у каждой была своя яркая картинка. Поразительно, но с тех пор - за двадцать лет - по теме XP, которая оказала огромное влияние на программную инженерию, в мире вышли всего 1-2 книги!

Не говоря уже о сильных, но контринтуитивных идеях из computer science, которые собираю для моих курсантов, но даже моё некогда любимое "Экстремальное программирование" уже давно не в моде. Сегодня выходит из моды вообще всё, не связанное напрямую с бездумным писанием гигабайтов кода (не имеющим, по большому счёту, никакого отношения к программированию). Каждый хочет заниматься всяческим "интересненьким"/"полезненьким", самыми последними технологическими причудами. Последняя технологическая причуда, которой я увлекался — это SmallTalk, и это то, что перестали разрабатывать примерно в 1980-м году. Это означает, что либо я сумасшедший, либо все остальные.

=

Я, безусловно, верю в мощь ООП и активно его применяю, и уверен, что с помощью ООП можно разрабатывать самые сложные системы быстро и надёжно. Однако правильным подходам надо учиться, причём куда больше, нежели работе с очередным веб-фреймворком или k8s, но этого почти никто не хочет, потому что кривой код ведь тоже худо-бедно работает на троечку с минусом. Очень многие, кстати, когда считают, что "думают" об ООП, думают на самом деле о Java или C++ (в СильныхИдеях рассказываю о правильных логических уровнях думания); фактически же люди думают о больших и громоздких структурах данных, основанных на длинных цепочках наследования, что к объектному мышлению не имеет отношения. Неудивительно, что такие подходы никому не нравятся.

"Иногда элегантная реализация - это функция. Не метод. Не класс. Не фреймворк. Просто функция." — Джон Кармак

=

Ok, самая главная архитектурная идея для любого ООП-проекта такая, что система просто должна норм работать, достигая своих целей, без какой-либо централизованной координации.

Первый объектно-ориентированный язык программирования Simula задумывался в 1960-е для моделирования сложных систем (термин "искусственный интеллект" тогда упоминался лишь в единичных научных работах), но его авторы мечтали о "естественном" создании сложных систем через организацию сотрудничества множества простых автономных агентов. Какая была для того времени глупая и романтическая идея!

И всё же эта идея лежит в основе ООП в том виде, в котором его изначально представляли себе Алан Кэй сотоварищи: "... есть разница между тем, что мы считали ООП-стилем, и поверхностной инкапсуляцией под названием "абстрактные типы данных"... Мягко говоря, мы были весьма поражены этим, поскольку для нас то, что тихо предлагала Simula, было чем-то гораздо более сильным, чем просто повторная реализация слабой и специфической идеи. Что я получил от Simula, так это то, что теперь вы можете заменить связи и присваивания целями... Объекты могут быть представлены как "территории" поведения более высокого уровня; они должны подходить для использования в качестве динамических компонентов".

=

Пояснение за Алана Кэя такое, что, во-первых, если мы используем декомпозицию, то потом очевидно обязаны будем использовать и композицию :) Однако композиция — это сборка системы из независимых модулей уже в совершенно других условиях, качественно отличных от условий, в которых создавались эти модули.

Например, классическое нисходящее проектирование Дейкстры "сверху вниз" подразумевает только декомпозицию по сути: разбиение сложной задачи на несколько более простых, к которым этот подход применяется рекурсивно, пока не получим "внизу" уже очевидные элементарные кирпичики. Однако этот подход не подразумевает следующего шага композиции, и в итоге получается архитектура, которая, как отмечает Мейер, будет отражать только личное понимание разработчиком какого-то отдельного механизма работы. А самое страшное, что в такой "архитектуре" получится некоторый жёсткий порядок действий (последовательность вызовов более простых функций), который всегда очень "хрупок" по определению: при любых изменениях требований к проекту потребуются сильные модификации такой архитектуры буквально на уровне кода — изменением последовательности подобных шагов. Это очень неэффективная схема, которая подразумевает негибкую модульную конструкцию, механически объединяющую группы функций в модули в полном отрыве от семантики домена.

Правильная методика проектирования должна "автоматически" охватывать

и декомпозицию в "онтологии домена", с сохранением его внутренней семантики, его содержания, формируя множество маленьких независимых "смыслов" — потому что форма (например, наборы типовых сценариев действий пользователей) всегда очень изменчива,

и композицию, в чём ООП прекрасно помогает, естественно стимулируя создание децентрализованной архитектуры — потому что мы делаем акцент не на порядке выполнения программы, а на конкретных возможностях нашей системы типов, нашего множества классов.

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


Источник: wiki.c2.com

Комментарии: