ГЛАВА 2. МЕТОДЫ И ПАТТЕРНЫ ПРОЕКТИРОВАНИЯ
Парадигмы проектирования. Возникновение проектирования программного обеспечения можно связать с появлением языков высокого уровня в 60-х и 70-х годах. Проектирование возникло как дисциплина, целью которой было управление сложностью программных систем и расширение возможностей разработчиков по созданию систем большего размера предсказуемого качества.
В развитии проектирования как дисциплины выделяют несколько этапов, в каждом из которых доминировала одна из парадигм проектирования. В 70-е и 80-е такой была структурная парадигма проектирования. Ее основу составляют нисходящие декомпозиционные методы, итеративно разделяющие систему на функциональные блоки, все более понятные и простые в реализации. Примерами таких методов могут служить метод постепенного уточнения (stepwise refinement) [6], метод структурного анализа и структурного проектирования SSA/SD [6], техника структурного анализа и проектирования SADT3. Среди восходящих методов структурного проектирования следует отметить метод структурного проектирования Джексона [6].
Преимущественные нотации моделей в этой парадигме – это структурная схема, схема потоков данных, диаграмма сущность-связь, диаграммы IDEF.
В 80-е и 90-е преобладающими стали методы объектно-ориентированного проектирования. Основой методов является выделение общего описания групп объектов и использование этого описания в качестве абстрактного типа данных. Различные эвристики выделения общего описания и процедуры создания модели нашли выражение в различных методах анализа и проектирования. В результате объединения Objectory, OMT и OOAD был создан унифицированный процесс разработки RUP и язык моделирования UML. Во многом благодаря обсуждению принципов проектирования на страницах таких журналов как C++ Report были сформулированы эвристики повышения изменяемости и сопровождаемости систем SOLID [7]. Следует также отметить методы проектирования, основанные на выделении и группировке обязанностей RDD и связанные с ними эвристики назначения обязанностей GRASP [8]. А также методы, построенные на прямом использовании модели предметной области для построения программной системы. Позднее они нашли отражение в книге Эванса по предметно-ориентированному проектированию [5].
Сейчас объектно-ориентированные методы применяются для разработки отдельных компонент сложных систем. Методы структурного проектирования нашли применение при разработке систем обработки данных, в проектировании архитектуры систем масштаба предприятия.
Систематизация и количественный подход. Существование разных подходов к проектированию в одних и тех же отраслях ведет к необходимости их сравнения и определения предпочтительного и указания границ применимости. Инициатива по выработке базовых методов и теории программной инженерии SEMAT ставит своей целью каталогизацию методов, выработке их описания на основе нескольких базовых понятий и, таким образом, создания основ накопления данных об использовании методов для их последующего сравнения.
Распространенный подход к накоплению знаний в области проектирования – составление каталога повторно применимых приемов решения часто встречающихся задач проектирования, которое могут быть адаптированы для разных случаев – паттернов проектирования. В конце прошлого века были составлены первые каталоги паттернов. Наиболее известным является набор из двадцати двух паттернов «банды четырех» GoF [3]. В сфере высокоуровневого (архитектурного) проектирования аналогом паттернов выступают архитектурные стили [9], комбинации которых, исходя из требований к системе, составляют первоначальное архитектурное описание.
Результатом процесса проектирования являются отраженные в модели решения по реализации программной системы. Для прогнозирования нефункциональных характеристик системы, в том числе сопровождаемости, переносимости и других, вводят показатели проектировочного решения (метрики дизайна). Показатели используют для количественного описания размера системы, сложности решения, выявления недостатков и потенциально проблемных мест в системе. В данной книге рассматриваются показатели сложности проектировочного решения Чидамбера-Кемерера [10].
Применение методов. До автоматического проектирования программных систем пока еще далеко, в проектировании остается существенная доля искусства. В том числе вследствие самой сути задач проектирования, относящихся к так называемым плохо поставленным задачам (wicked problems), условия которых неполны, противоречивы, меняются со временем и в процессе решения. Тем не менее, владение базовыми методами позволяет не отвлекаться на обдумывание задач, решение которых уже известно, и, таким образом, повысить качества результата и сократить время его создания.
Рассматриваемые в сборнике методы проектирования отражают современное состояние практики разработки и проектирования на уровне компонентов и приложений. Их изучение позволит как вступить в специальность проектирования, восполнить отдельные пробелы, так и по-новому взглянуть на уже известную область.
Принципы проектирования классов и интерфейсов SOLID. Аббревиатура SOLID расшифровывается по первым буквам сокращений названий принципов проектирования классов.
Single responsibility principle (SRP), принцип ограничения обязанностей, говорит, что модуль или класс должен иметь только один набор функционально сходных обязанностей.
Open-closed principle (OCP), принцип открытости-закрытости, указывает, что класс или модуль должен быть расширяем (открыт для расширения) без внесения в него изменений (закрыт для изменения).
Liskov substitution principle (LSP), принцип подстановки (описан в статье Барбары Лисков, отсюда название), указывает правило построения иерархии типов так, что любой подтип или дочерний класс подставим вместо базового типа или класса соответственно.
Interface segregation principle (ISP), принцип разделения интерфейса, говорит, что для обозначения разных ролей, которые играет класс в разных взаимодействиях, следует использовать разные интерфейсы.
Dependency inversion principle (DIP), принцип обращения зависимостей, указывает на корректное применение принципа сокрытия информации в объектно-ориентированном подходе к проектированию, когда зависимости направлены от реализации класса к выделенным абстракциям: описаниям типов данных или интерфейса
§4. РАСШИРЕННЫЕ КЛАССЫ И ОБЪЕКТЫ
ОСНОВНЫЕ ПОНЯТИЯ
Квалификатор (qualifier) используется для разделения всех связей ассоциации на подмножества согласно уникальным ключам. Обычно в бинарной ассоциации «один-ко-многим» по ключу выделяют связи «один-к-одному».
Класс ассоциации (association class) используют, когда логическое отношение между объектами обладает сложной структурой или поведением. Класс ассоциации является одновременно и ассоциацией, и классом, поэтому может иметь свойства и операции.
Напомним, что операция – это поведенческая черта класса, которая определяется именем, набором параметров, их типами и кратностями, типом и кратностью возвращаемого значения. В дополнение к этому, можно задать ограничения на реализацию операции: предусловие (precondition), постусловие (postcondition), ограничение возвращаемого значение (body condition).
Если операция не изменяет значения свойств класса, то к операции добавляется украшение запрос (query).
Каждый параметр операции может иметь имя, тип, множественность. Параметру можно задать направление параметра (in, out, inout, return), значение по-умолчанию, ограничение на множественный параметр: упорядоченность значений (ordered), уникальность значений (unique).
Производные свойства класса (derived property) вычисляются на основе других свойств или результатов вызова операций класса или других классов модели. Способ вычисления значений свойства указывается вместе с определением самого свойства, при этом вычисление значений не должно иметь сторонних эффектов и должно быть идемпотентным (повторные вычисления дают тот же результат при неизменности остальной модели). Часто используются производные свойства, значения которых составлены из объединения union множеств значений свойств, выделяющих подмножества subsets в базовых свойствах.
Шаблонные классы (template class) по сути не являются полноценными классами, а только их заготовками, определенными с точностью до значений параметров шаблона. Шаблонным может быть не только класс, но и другие элементы модели. Операция присвоения значения параметру называется связыванием (bind). По смыслу, связывание класса с шаблонным классом с приданием значения параметру аналогично созданию класса из шаблона с указанным значением параметра и наследованием от этого класса.
Переопределение (derived) позволяет заменить определение черты классификатора в рамках его контекста переопределения (redefinition context), составляющего совокупность всех классов, обобщающих данный. При переопределении можно заменить, например, сигнатуру операции. После переопределения при обращении к новой операции в классе следует использовать новую сигнатуру. При этом наследованная переопределенная операция также доступна для использования.
Множество обобщения (generalization set) позволяет логически группировать отношения обобщения. Можно указать, что в данном множестве обобщений приведены все возможные уточнения базового класса, или что совмещение непосредственно уточняющих классов в одном экземпляре или подклассе не допускается.
Супертип (powertype) используется совместно с множеством обобщений. Супертип это классификатор, экземплярами которого являются классы-элементы множества обобщений.
Пакеты (package) позволяют группировать элементы модели под общим именем. Для обращения к элементу пакета необходимо использовать квалифицированное имя, состоящее из имени пакета и имени элемента.
Между пакетами определены отношения доступа «access», которое для элементов пакета делает доступным элементы указанного пакета без необходимости указания квалифицированного имени, и отношение импорта «import», которое аналогично доступу, но делает элементы также доступными при последующем импорте или доступе из другого пакета.
Отношение объединения пакетов (merge) «merge» позволяет объединить в одном элементе определения этого элемента в других пакетах.
Сигналом (signal) называют особый вид классификатора, экземпляром которого является сообшение, передаваемое асинхронно отправителем получателю или группе получателей. Для того, чтобы обрабатывать сигналы, получатель должен быть активным классом (active class), объявлять черту поведения – получение сигнала (reception), с которой может быть связан метод, либо определять собственное поведение, которое обрабатывает поступающие сигналы.
ЗАДАЧИ
4.1. На рис. 11 представлены шаблонные интерфейсы Map и Entry. Интерфейс Map позволяет по ключу типа K получить значение типа V. Интерфейс Entry представляет собой пару значений.
а. Измените модель так, чтобы шаблон Entry использовал параметры шаблона Map.
б. Определите интерфейс Map_StringInteger, который указывает String типом ключа и Integer типом значения в шаблоне Map.
в. Сколько операций содержит интерфейс Map_StringInteger? Ответ поясните.
4.2. Диск Disk содержит несколько папок Folder, которые могут содержать файлы File и папки. Произведения Composition хранятся на дисках в виде файлов.
а. Используя классы ассоциаций, постройте модель хранения произведений на дисках.
б. Дополните модель, укажите, что произведение может быть картинкой Picture, либо музыкой Music, либо фильмом Movie.
в. Может ли произведение храниться на одном диске в разных файлах? Ответ поясните.
г. (*) Сравните способы реализации в модели хранения произведения в нескольких файлах на одном диске. Приведите примеры на диаграмме экземпляров.
4.3. На заседании Meeting обсуждается discuss не менее одного вопроса Issue. Вопрос может быть посвящен обсуждению артефакта Artifact. В каждом вопросе должно быть указано текстовое название, числовой код и имя автора.
а. Добавьте в модель вопрос по постановлению, отдельный вопрос и сложный вопрос.
б. Укажите, что постановление Resolution связано с вопросом по постановлению, как тема topic, и с несколькими артефактами documents.
в. К сложному вопросу примените паттерн Composite так, чтобы сложный вопрос включал несколько других вопросов.
Конец ознакомительного фрагмента.