Про абстрагирование, слабосвязную архитектуру и проектирование в целом

    К хорошим постам «Код в стиле «дамп потока сознания»» и «Микро-рефакторинг, о котором мы так часто забываем».

    • Почему большинство программистов не любят «читать чужой код»?
    • Почему рефакторинг и внесение изменений становятся серьезной проблемой?
    • Почему так часто случается, что легче переписать с нуля?
    • Почему одни программисты называют других хорошими или плохими словами?


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


    • Лапша
    • Абстрагирование
    • Слабосвязная архитектура
    • Код из головы?


    Лапша


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

    Казалось бы, можно возрадоваться, но есть некоторые обстоятельства из суровой реальной жизни, не заметных за розовыми очками.
    1. Любая программа содержит ошибки. Поэтому ее нужно сопровождать.
    2. В любую программу рано или поздно нужно вносить изменения. Поэтому ее нужно сопровождать.
    3. Любая программа или ее части могут стать компонентами другой программы. И оригинальные решения придется сопровождать.

    Каждую программу имеет смысл писать с учетом того, что некто будет сопровождать ее. Ваш код будут читать другие программисты, и только по вашему коду они будут делать выводы о вашем моральном облике. И очень часто этим «другим программистом» будете вы сами. Аксиома, не правда-ли?

    Всем же приходилось возвращаться к анализу собственного кода?

    Рассмотрим типичный пример — крутая программа, где весь код свален в одну кучу.



    Так бывает, когда в одном компоненте одновременно находятся:
    — финальный вывод и получение данных;
    — обработка данных заведомо различной природы;
    — развернутые алгоритмы в телах вложенных условий.

    На самом деле, казалось бы, зачем выносить в отдельный метод (функцию) код в пару строк? Для кого-то это будет удивительным, но смысл в этом есть.

    Абстрагирование


    Сильнейшим приемом программирования является абстрагирование — разделение программы и всех ее компонентов на части.

    • Бизнес-логика приложения распределяется по системе классов, исходя из предметных сущностей, функционала, этапов и типов обрабатываемой-передаваемой информации (MVC)
    • Приложение состоит из нескольких практически несвязанных компонентов. Часто они даже пишутся на разных ЯП, и взаимодействуют через API. Например, для толстой игрушки — слой БЛ, физика, звук, растеризатор. Физика и графика — Ogre на С++, бизнес-логика, GUI и звук — Lua или Python. Для веб-сайта — раздельные компоненты веб-сервера, интерпретатора, CMS на интерпретируемом языке, отдельных программ для обработки изображений и видео, драйвер и движок БД.
    • Внутри одного класса разделение действий на разные методы (функции) по функциональному признаку. Отделены приватные методы, хотя бы для того, чтобы обозначить процессинг данных, актуальный только для внутренних целей класса, и информацию, имеющую значение и ценность для пользователя класса.




    Слабосвязная архитектура


    Слабосвязная архитектура — это медвежья сила в программировании. Именно благодаря ей на свет появились, например, такие хорошие вещи, как CodeIgniter, YII, jQuery, Chomium, Half Life, Counter Strike — список огромен.

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

    Например, в классе выделяется одна или несколько, заведомо малое количество «диспетчерских» функций, которые для выполнения задач вызывают другие, в том числе приватные методы класса.

    Высокоуровневые примеры известны всем пользователям фреймворков — есть наборы компонентов, которые можно применять или не применять в зависимости от реальных потребностей.

    Ключевым звеном слабосвязной архитектуры является выделение центрального компонента. В качестве такого решения может выступать даже формализованное API. Естественно, при разработке нового продукта API может и будет развиваться вместе с разработкой, и неоднократно пересматриваться.



    Тут есть еще одна сильная сторона — именно центральному компоненту имеет смысл посвящать большое количество внимания, и таким образом вероятно экономится время на документирование и комментирование остальных. Сама по себе возможность расставить приоритеты позволяет экономить время.

    Удачным решением является применение слабосвязной архитектуры на различных уровнях приложения. Тогда даже самая сложная схема Data Flow будет легкочитаемой.



    Код из головы?


    Всегда присутствует сооблазн сесть и написать решение одним махом, из головы. Сам этим часто грешу.

    На этапе написания кода в большинстве случаев проблем не возникает. Проблемы появляются потом, что освещено в начале статьи.

    Главным неприятным последствием всех проблем является потери времени. Зачастую эти потери времени огромны и фатальны по своим последствиям. А предупредить и минимизировать их не так уж сложно.

    Тут имеет смысл сделать паузу и набрать в гугле фразу «проектирование программ». Все найденные методы будут графическими, простыми и доступными для понимания и использования.



    Смысл прост:
    — уделив большее внимание анализу задачи,
    — отделив при анализе методы (в том числе программирование, фреймворки, алгоритмы) от целей,
    — не выделяя слишком большой приоритет какому-то одному из элементов на схеме выше («нам важно все»),
    … экономим огромное количество времени, денег, сил, а также прокачиваем собственную репутацию в глазах коллег.

    Проектирование программ — не менее важный и полезный этап, чем освоение ЯП, знакомство с библиотеками, фреймворками и технологиями. Можно весьма сильно продвинуться вперед, и эффективно решить проблемы, обозначенные выше, уделив достаточное внимание этому прекрасному аспекту программирования.
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 59
    • +1
      У многих разработчиков важность этапа проектирования недооценена. Шире надо мыслить!
      По моему мнению, на проектирование нужно закладывать в 2-3 больше времени, чем на реализацию.

      Проектирование — это творчество, а реализация — это тупо ремесло.
      • +10
        В теории, теория и практика — одно и тоже, но на практике — это совершенно разные вещи. (с) Эйнштейн.
        • +13
          Прошу прощения за занудство, но автор этого высказывания — не Эйнштейн, а Jan L. A. van de Snepscheut.
          Оригинал: In theory, there is no difference between theory and practice. But, in practice, there is.

      • +3
        Все таки позанудствую — YII и CI — это скорее примеры сильно-связанных архитектур.
      • +5
        Как-то очень упрощено всё. А ваше абстрагирование так и чешется язык назвать декомпозицией. Абстрагирование (техническое) для меня процесс следующий за декомпозицией. Когда отдельные компоненты мы не просто выделяем, а выделяем через интерфейсы/публичные контракты, абстрагируясь от реализации, имея возможность её менять ничего больше не трогая.

        И к последней схеме претензии есть — бизнес-логика почему-то с предметной областью связана через UI, а с сущностями не связана. Это как? Как по мне, так бизнес-логика непосредственно оперирует сущностями, а в ООП зачастую методами объектов сущностей и ограничена. И с UI взаимодействует поскольку постольку. Проще говоря, сущности+бизнес-логика=модель, UI=вид/представление, а, видимо, функциональная логика=контролер, если брать MVC.
        • +7
          Статья абсолютно не понравилась. Смесь общих, всем понятных фраз с какимито спорными высказаваниями, как быдто филологу попросили написать реферат об архитектуре.
          Ключевым звеном слабосвязной архитектуры является выделение центрального компонента
          — это вообще в некакие рамки не вкладываеться…

          PS: с другой стороны прекрасная статья, вызавающая кучу неготивних имоцей и желания похоливарить ;)
          • 0
            Может быть, под центральным компонентом подразумевается Composition Root? Только зависимости на диаграмме неправильно нарисованы. Это Composition Root должен знать обо всех классах приложения, а сами классы ничего не знают о Composition Root.
            • 0
              Composition root нужен доя inversion of control, а не для слабо связанных архитектур.
              • +1
                Не представляю себе слабую связанность без инверсии зависимостей.
                • 0
                  Сейчас никто не представляет. Но по факту это не обязательные вещи (а понятие «слабой связанности» — относительно, а не абсолютно).
                  • 0
                    Можете раскрыть тему?
                    • 0
                      Что именно?
                      • 0
                        Про относительность понятия слабой связанности.
                        • 0
                          Эмм.

                          Вы считаете, что «слабая связанность» — это флаг (либо слабая, либо сильная), а не относительная характеристика (слабее-сильнее)?
                          • 0
                            Да, я ни разу не встречал связанность средней силы :)
                            • 0
                              Одна захардкоженная зависимость слабее чем 10, а 10 слабее чем 20 :) Или, как вариант, 10% захардкоженных зависимостей слабее чем 50%, а 50% слабее чем 100% :)

                              • +1
                                А, теперь понятно. Вроде средней температуры по больнице… Например, наш проект слабо связан на 90%. Планируем ослабить его до 146% в этом году.
                                • 0
                                  :) Нет, что-то вроде «господа, вам не кажется, что наш проект слишком сильно связан? Давайте ка на 50% уменьшим число связей»
                                  • 0
                                    «Господа, наш проект более-менее связан. Давайте сделаем его ещё менее связанным!»
                                    Интересно, так будет лучше?
                                    Кстати, идеалом тогда станет полное отсутствие связей!
                                    • 0
                                      Полное отсуствие связей это пул сообщений.
                                      • 0
                                        Нет, пул сообщений — это приравнивание числа связей к числу компонент.
                                        Полное отсутствие связей — это неиспользуемый код )
                              • 0
                                McConnell, Code Complete, 2nd edition, глава Keep Coupling Loose (страницы 100-102), выделение мое:

                                A routine like sin() is loosely coupled because everything it needs to know is passed in to it with one value representing an angle in degrees. A routine such as InitVars( var 1, var2, var3, ..., varN ) is more tightly coupled because, with all the variables it must pass, the calling module practically knows what is happening inside InitVars(). Two classes that depend on each other’s use of the same global data are even more tightly coupled.
                                [...]
                                A routine that takes one parameter is more loosely coupled to modules that call it than a routine that takes six parameters. A class with four welldefined public methods is more loosely coupled to modules that use it than a class that exposes 37 public methods.
                                [...]
                                In short, the more easily other modules can call a module, the more loosely coupled it is…


                                Видите сравнительные степени («сильнее связано», «еще более сильно связано», «слабее связано»)?
                                • 0
                                  Вижу, но здесь немного о другом речь. Я имел в виду физические ссылки на конкретные классы/модули. Они либо есть, либо их нет.
                                  • 0
                                    Но связность (coupling) ими не ограничивается.

                                    (да и в этом случае степень связности все равно может быть различной)
                                    • 0
                                      Помимо структурной связанности есть семантическая связанность.
                • +1
                  Статья хороша тем, что наглядно показывает разбиение системы на модули в общих чертах. Однако самое сложное, на мой взгляд, заключается во взаимодействии этих модулей, в том, что на картинках обозначено стрелками. Модули относительно легко проецируются в формат реального мира. Взаимодействие же требует более глубоких раздумий.
                • +2
                  В слабосвязаной архитектуре не всегда есть центральный компонент. Я бы даже сказал такого нужно избегать. В каждом слое может быть несколько ключевых сервисов, каждый из которых выполняет только свои роли.
                  • +2
                    Спасибо за статью!
                    Хотя, статью можно было бы и не читать.
                    Достаточно былы бы картинки посмотреть, кроме последней. ;)
                    • +1
                      Каждую программу имеет смысл писать с учетом того, что некто будет сопровождать ее… Аксиома, не правда-ли?

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

                      Остальные пункты доставляют не меньше.

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

                      Такие красивые и понятные диаграммы типа вашей «ясной структуры» получаются только у двух типов программистов — у преподавателей-теоретиков и у впаривающих свои книжки консультантов. В реальности обычно всё немного не так радужно.

                      В-общем, удачи автору:)
                      • +1
                        Если я правильно понял, до здравствует Inversion of Control
                        • 0
                          Правильно, до здравствует :-)
                        • +2
                          Всегда присутствует сооблазн сесть и написать решение одним махом, из головы

                          У вас просто слишком простые задачи.
                          • +2
                            Или голова хорошая
                            • +1
                              для любой даже супер гениальной головы есть сложные и непосильные задачи.
                              • +1
                                Жаль, что не каждому посчастливится их решать.
                          • 0
                            А теперь попробуйте удержать 22 сущности из «Ясной структуры»
                            • 0
                              Лично я себе представляю Big Picture хорошей архитектуры приложения как множество маленьких, слабо связанных абстракций и классов, из которых, как дом из кирпичей, мы собираем приложение. Т.е. делаем композицию классов в рантайме, в одном месте, например, с помощью IoC контейнера.
                              • 0
                                Гораздо сложнее представить, как эта композиция поведет себя в рантайме. Зачастую не совсем так, как задумано.
                                • +2
                                  Это точно. Для крупных проектов просто необходима визуализация графа зависимостей, иначе никто не разберется как оно все работает)
                                  • +1
                                    Интеграционные тесты не?
                                    • 0
                                      Не.
                                      • 0
                                        А что мешает?
                                        • 0
                                          Иногда код написан таким образом, что число веток растет по экспоненте.
                                          В этом случае тесты не помогут — такое число веток ни один тест не покроет, либо тест окажется сравним по сложности с самим кодом.
                                          В этих случаях, как правило, необходимо пересматривать код на предмет упрощения логики и разбиения на зап. части, которые можно будет тестировать независимо.
                                          Да и вообще, если писать код, ориентируясь на его тестирование, он получается много проще… и писать его на порядок сложнее.
                                          • 0
                                            Число веток чего? Композиции зависимостей? Смешно.

                                            Для того, чтобы разбивать на части, эта самая композиция и нужна, так что вы просто не отследили, в какую именно ветку отвечаете.
                                            • 0
                                              Пример — компонентная архитектура, со связями, основанными на событиях.
                                              Код компонентов относительно (системы) не сложный, однако при наличии множества получателей или генераторов одинаковых событий структура runtime-связей будет далеко не тривиальной.
                                              Как правило, интеграционные тесты в этих случаях покрывают только наиболее частые и значимые ветки.
                                              • 0
                                                Хм. Интеграционные тесты для такой системы должны, по идее, тестировать только то, что компонент генерирует событие определенного типа с определенными параметрами в ответ на событие другого определенного типа с другими определенными параметрами. То есть создаем фэйковый (стаб) генератор событий, фэйкового (мок) слушателя, регистрируем их в системе, вызываем у стаба метод генерации события с данными для тестируемой ветки, и проверяем, что мок получил ожидаемое событие с ожидаемыми данными. Всё, проверили, что этот компонент для данной ветки интегрирован в систему правильно, то есть правильно зарегистрирован, правильно принимает события и правильно обрабатывает их. И так для каждой ветки.

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

                                                А уровень проверки рантайм-связей между компонентами — это уже уровень функциональных/приемочных тестов, которые пишутся на основе сценариев использования.

                                                • 0
                                                  Согласен, перепутал терминологию.
                                                  Я имел в виду покрытие сценариев функционально/приемочными тестами.
                                                  • 0
                                                    А эти тесты, если есть хоть какие-то, то, по-моему, просто обязаны покрывать всю функциональность, описанную в ТЗ (явном или нет — не суть).
                                  • +1
                                    Центральный компонент в слабосвязной архитектуре несколько режет слух и намекает на антипаттерн «God Object». Хороший пример: GIT
                                    • +2
                                      Насколько мне известно, абстрагирование — это в первую очередь рассмотрение объекта, отбрасывая несущественные для выполнения задачи детали, а разделение программы на части является лишь побочным следствием абстрагирования. Мне кажется, что вместо абстрагирования стоило сослаться на «сильное зацепление» (про которое, как раз, часто и рассказывают в паре со «слабой связанностью»).

                                      Про центральный компонент рассказано очень смутно. Это можно понять как то, что автор рекомендует создать один центральный компонент, который надо перегружать обязанностями (создавать «божественные» объекты). И лишь из схемы «Ясная структура» можно догадаться, что автор всё таки рассуждал о распределённой системе и «центральным компонентом» называл не более, чем одно из звеньев распределённой системы.

                                      Говоря про слабосвязанную архитектуру, не лишним было бы упоминуть про важность распределения обязанностей и правильную структуру связей (ведь на схеме показана именно плохая структура связей, возникшая, судя по смыслу статьи, в следствии неверного распределения обязанностей).

                                      > Тут есть еще одна сильная сторона — именно центральному компоненту имеет смысл посвящать большое количество внимания, и таким образом вероятно экономится время на документирование и комментирование остальных.

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

                                      P.S.: плюс, правильнее, неверное, называть «связанная», а не «связная», т.к. я часто встречал, что под «связностью» подразумевают перевод английского термина «cohesion», а не «coupling».
                                      • 0
                                        Подскажите пожалуйста, а в какой программе делали схемы? Извиняюсь, что не по теме.
                                        • 0
                                          Impress, есть в Open Office и в Libre Office.

                                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.