Пользователь
0,0
рейтинг
22 февраля 2013 в 01:36

Разработка → Иерархия принципов проектирования, или самые важные слова для инженеров

В этой короткой заметке я хотел бы систематизировать (а именно, расположить в иерархию) многие популярные принципы проектирования программных приложений (test-driven development, ООП, SOLID и т. д.), а также рассмотреть следствия из этой иерархии.

В частности, такая иерархия (я надеюсь) позволит лучше расставлять приоритеты в разработке и профессиональном росте, лучше понимать старые технологии и быстрее изучать новые. При появлении новой парадигмы разработки (a la test-driven development) вы сможете быстро включить ее в эту иерархию и, следовательно, быстрее понять, из каких принципов исходили создатели парадигмы и как правильно ее использовать. Новичкам в программировании статья может быть полезна как обзор существующих принципов.

И в качестве самого базового я полагаю разумным считать принцип «управления сложностью/минимизации технической сложности» МакКоннела. А самыми важными срествами минимизации сложности являются модульность и абстракция.

Эта короткая статья будет состоять из нескольких разделов:
  1. Правило построения иерархии
  2. Обзор иерархии
  3. Замечания
  4. Следствия

Правило построения иерархии


Базовое правило для расположения принципов проектирования в иерархию будет следующим: если вы можете следовать принципу А, но отказаться или не знать о приеме B, то A является более базовым принципом проектирования. Например, вы можете разобраться с ООП, но пока не прочитать о шаблонах проектирования Gang of Four—это не помешает вам писать программы с использованием ООП; поэтому ООП является более базовым принципом. Или, например, вы можете отказаться от ООП (или от каких-то его атрибутов—например, наследования и полиморфизма), но, тем не менее, продолжать писать модульные программы на С с четкими интерфейсами модулей и уровнями абстракции. Поэтому модульность и абстракция являются более базовыми принципами, чем ООП.

Обзор иерархии


Итак, некоторые из тех немногих принципов проектирования, известных мне, я предпочитаю располагать на следующих уровнях:
  1. минимизации технической сложности
  2. модульность и абстракция; Keep it simple, stupid (KISS); Don't repeat yourself (DRY)
  3. процедурное программирование; ООП; test-driven development (TDD); domain-driven design (DDD)
  4. шаблоны проектирования (Фаулера, Банды Четырех, MVC); SOLID

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


Замечания


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

Минимизация технической сложности

Что такое управление сложностью / минимизация технической сложности (с сохранением необходимого функционала, разумеется) и почему ее можно считать базовым принципом проектирования, можно узнать в прекрасной книжке Стива МакКоннела «Совершенный код» («Code Complete»), глава 5. Обсуждение начинается именно с термина «управление сложностью», но лично мне он кажется менее информативным.

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

Модульность и абстракция, или самые важные слова для инженеров

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

Поэтому хотелось бы подробнее остановится на модульности и абстракции—понятиях, рассматриваемых в отличной книге «Structure and Interpretation of Computer Programs» («Структура и интерпретация компьютерных программ», SICP; первое издание в свободном доступе, второе—нет)—и процитировать введение к этой книге, которое, на мой взгляд, содержит в себе самые важные слова для инженеров.

Самые важные слова для инженеров
«We control complexity by building abstractions that hide details when appropriate. We control complexity by establishing conventional interfaces that enable us to construct systems by combining standard, well-understood pieces in a “mix and match way”. We control complexity by establishing new languages for describing a design, each of which emphasizes particular aspects of the design and deemphasizes others».

Еще раз: модульность и абстракция—основные способы управления сложностью; и это, на мой взгляд, самые важные идеи о разработке ПО и инженерной деятельности вообще, что мне довелось встречать (если кто-то читал еще интереснее—добро пожаловать в комментарии). Что может сравниться с наслаждением от сознания того, что вам не нужно помнить, как работает внутри используемый класс (или даже библиотека), в каком порядке вызывать его методы, какие ограничения накладываются на параметры?—потому что это все не имеет значения, ведь класс формирует правильную абстракцию и избавляет пользователя от деталей: методы можно вызывать в любом порядке, их семантика и семантика параметров очевидна, конфликтов с одновременно используемыми библиотеками не может быть и т.п.

Если вы хотите глубже проникнутся этими принципами, горячо рекомендую вам SICP и, как водится, МакКоннела. [Ремарки: конечно, многим эти книги будут известны, а цитата из SICP покажется банальной, но среди, по крайней мере, моих знакомых, профессиональных и высококвалифицированных программистов, очень многие не слышали о SICP—кто-то прогулял лекции, а кто-то учился на физика. И если вы знаете аналогичную книжку, но посовременне—добро пожаловать в комментарии].

Test-driven development и domain-driven design

Конечно, test-driven development и domain-driven design сложно представить без использования ООП, и, следовательно, изучать их необходимо после ООП, поэтому их можно было бы расположить на одном уровне с шаблонами проектирования.

С другой стороны, test-driven development вполне можно применять для программ на С, в которых не используется ни полиморфизм, ни наследование, но при написании которых можно проектировать архитектуру c расчетом на тестирование отдельных процедур и писать такие тесты. То же относится и к domain-driven design: программа может быть написана на C, но совершенно в духе DDD. Именно поэтому они помещены на один уровень с ООП.

Следствия


Правильные приоритеты в разработке

Если архитектура программы изобилует шаблонами проектирования и модными штуками типа Dependency Injection/Inversion of Control, это еще не значит, что программу будет легко понимать и сопровождать. Причина кроется в том, что есть более базовые принципы проектирования—модульность и абстракция, и архитектор может легко упустить их из виду в погоне за шаблонами. Помня об этой иерархии, вы как архитектор больше не допустите подобную ошибку, а как разработчик всегда сможете легко вербализовать для архитектора, почему именно он не прав, совершая такую ошибку.

Правильные приоритеты в профессиональном росте и обучении (младших сотрудников или студентов)

Иерархия принципов проектирования, я надеюсь, сделает очевидным несколько фактов:
  1. прежде чем рекомендовать младшему инженеру книжку про ООП, лучше дать книжку про модульность и абстракцию (например, SICP)
  2. прежде чем рекомендовать книжку про шаблоны проектирования, лучше дать книжку про ООП
  3. и т.п.

Лучшее понимание существующих технологий

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

Всем спасибо за внимание!

P.S.


Вообще, раз уж я рекламировал SICP, называемую также Книгой Мага, в этой заметке, то надо для полноты картины порекомедовать Книгу Золушки и Книгу Дракона, являющиеся не менее фундаментальными. Итак, список сказочных книжек:
  1. Книга Мага (бесплатная версия, второе издание на амазоне)
  2. Книга Золушки
  3. Книга Дракона

Опять же, многим эти книги покажутся банальными (особенно Книга Дракона), но, я полагаю, найдется и немало людей, кто впервые о них прочитает в этой статье.
Василий Баранов @Bas1l
карма
98,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (8)

  • +8
    Да уж, «Книга Дракона» прямо-таки образец банальности.
    • 0
      Обложку в новом издании испортили основательно.
  • +5
    После списка литературы я начал активно ощущать собственную неполноценность.
  • +1
    > прежде чем рекомендовать младшему инженеру книжку про ООП, лучше дать книжку про модульность и абстракцию (например, SICP)
    Это Вы хорошо сказали. Еще лучше — не зацикливаться на заветных трех буквах (ООП). Спасибо за интересные «сленговые» названия книг (про мага с Золушкой впервые услышал).
    • 0
      Всегда пожалуйста)
  • +2
    Я считаю немного иначе.

    Мне больше представляется модель «вход-выход» (модель черного ящика), если мы говорим про большие приложения со сложной архитектурой.

    Это бывает полезно при работе на любых уровнях. Как подход к мышлению.

    То есть существуют
    — задачи, которые стоят перед какой-либо методологией, подходом, сущностью
    — способы решения этих задач
    — критерии оценки эффективности этих задач, метрики ПО (см. «Rapid Software Development», Dr. Bob Martin)

    К примеру
    1. Вход У вас на входе проблема — статическая зависимость одного класса от другого.
    2. Выбор способа решения (из нескольких!). Вы выбираете принцип инъекции зависимостей. Берете подходящий паттерн — Strategy. Рефакторите.
    3. Смотрите. В итоге получилась инъекция в метод, и первый класс теперь зависит от интерфейса (принимает на вход любую реализую интерфейса), и второй класс также становится зависим от интерфейса, он теперь обязан его реализовывать :)
    4. Замеряем, смотрим. Изменилась цикломатическая сложность, связанность уменьшилась, но возросло количество классов и методов :)

    Это сильно утрированно. Просто когда вы что-то применяете, вы должны понимать, зачем это делаете, что улучшаете в архитектуре и что ухудшаете. Одно лечит, другое калечит — это воистину так, и балансу посвящен вечный рефакторинг. Несколько god Object — трудно поддерживать, но работает быстро. Миллиард микроклассов — поддерживать легко, но в некоторых языках начинает тормозить. Условно :)

    А то просто — «а зачем тебе тут абстрактный класс» — " а лишним не будет, зато красиво",
    или там «зачем тут такая высокая связанность между методами? »… невразумительное что-то...". Прямо «а еще я туда ем» (С)

  • +1
    Здесь можно найти хорошо отформатированный PDF (второго издания?) SICP: sicpebook.wordpress.com/.

    Кстати, она существует и в русском переводе, довольно качественном, но в мягком переплете.
    • 0
      С англ у меня пока не сложилось, но русский перевод мне не понравился вообще, например в самом начале книги: «Действия, в которых ум проявляет свои способности в отношении своих простых идей, суть главным образом следующие три: 1) Соединение нескольких простых идей в одну сложную; так образовались все сложные идеи». Может дальше лучше, но я попробую читать в англ варианте.

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