Wednesday, July 28, 2010

Гранулярность. Принципы проектирования модулей

В предыдущем посте я рассмотрел пять основных принципов проектирования классов, сформулированных Робертом Мартином.

Сейчас я хочу подробно разобрать следующие три принципа из списка Мартина, касающиеся проектирования модулей. Эти принципы позволяют правильно разделять функциональность между модулями, управляя тем самым сфокусированностью (cohesion) функционала в каждом модуле.

REP. Reuse / Release equivalence principle

Гранулярность повторного использования эквивалентна гранулярности пакетирования. Пакет должен пройти полный цикл разработки (от проектирования до тестирования).

Повторное использование кода – одна из основных целей ООП. Давайте определимся, что означает «повторное использование». Тут все достаточно просто. Если вы добавляете себе в проект исходный код, написанный кем-то другим, то это не есть «повторное использование». Неважно как вы добавите себе чужие исходники:

  • просто скопируете интересующий вас метод или класс
  • скачаете из интернета и добавите себе в солюшн отдельным проектом
  • стянете из системы контроля версий и опять же добавите себе отдельным проектом

Во всех этих случаях вы придумываете себе ненужные заботы.

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

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

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

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

Итак, давайте вернемся к нашему первоначальному вопросу. Что же означает «повторное использование»? Повторное использование по определению Мартина – это когда у себя в проекте вы используете функционал, написанный кем-то другим и при этом у вас нет необходимости видеть чужие исходники. Все что вам нужно, это подключить себе скомпилированную сборку (пакет) и знать ее публичный API.

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

Итак, согласно REP, единицей повторно используемого функционала следует считать только бинарную сборку (пакет), которая прошла все стадии разработки от проектирования до тестирования, имеет определенную версию и самое главное гарантирует вам изолированность от ее исходников.

CRP: Common Reuse Principle

Классы в пакете используются совместно. Даже если вы используете всего один класс из пакета, вы используете все его классы.

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

Этот принцип, как ISP для классов, требует включать в модули только минимально необходимый объем функционала, избавляясь от классов с которыми нет явных связей.

CCP: Common Closure Principle

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

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

Ссылки:

No comments:

Post a Comment