КАТЕГОРИИ:
АстрономияБиологияГеографияДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРиторикаСоциологияСпортСтроительствоТехнологияФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника
|
Пример построения диаграммы классов.Задание: Построить диаграмму классов DOM демонстрационно-обучающей системы решения квадратных уравнений. Шаг 1. «Вначале было слово…» Квадратное уравнение имеет вид ax2+bx+c=0. Очевидно, что на данном этапе имеем один класс «квадратное уравнение». Используя русско-английский словарь и нотацию Borland, назовем его TQuadraticEquation. Полями (данными) класса будут коэффициенты уравнения, методами (функциями) – функция вычисления корней. Таблица 1
Рис 1. Диаграмма классов на первом шаге. Шаг 1.1. Поскольку программа демонстрационно-обучающая, очевидно, что ход вычислений пользователю будет интересен. Следовательно, класс TQuadraticEquation нужно расширить методами, позволяющими получать промежуточные результаты вычислений и получить данные для постройки графика. Таблица 1.1
Оценка модели: Очевидно, что данная модель не соответствует принципам high cohesion (сильного сцепления) и единственности ответственности, поскольку класс несет как функционал описания квадратного уравнения, так и функционал для его решения и для построения графика. Класс перегружен методами, что затрудняет понимание и сопровождение. А при изменении алгоритма решения или добавления нового алгоритма (например, теоремы Виета), класс необходимо будет дополнить (как? еще дополнить?!) новыми методами, что является нарушением принципа открытости/закрытости. Рис 1.1. Окончательная диаграмма классов на первом шаге. Вывод: Необходимо разделить «зоны ответственности» между различными классами. Кроме того, неплохо было бы дополнить классы конструкторами и деструкторами. Шаг 2. «Раз ромашка, два ромашка…» Выделим зоны функциональности и разделим их между соответствующими классами (и да поможет нам электронный словарь ABBY Lingvo 12): · Описание квадратного уравнения (класс TQuadraticEquation); · Алгоритм решения квадратного уравнения (класс TQuadraticSolution); · Получение данных для постройки графика (класс TQuadraticGraphData). Класс TQuadraticEquation будет связан («ассоциирован») с классом TQuadraticSolution и с классом TQuadraticGraphData (Очевидно, что решить уравнение без самого уравнения или построить график функции без функции нельзя.). Классы TQuadraticSolution и TQuadraticGraphData на данном этапе между собой не связаны (решение и построение графика не зависят друг от друга). Считая само уравнение частью процесса решения и построения, получим: · TQuadraticSolution агрегирует TQuadraticEquation. Поскольку уравнение может иметь много способов решения, а «решение» может в один конкретный момент может решать только одно уравнение, то кратность связи будет «один» со стороны класса TQuadraticEquation и «много» со стороны класса TQuadraticSolution. · TQuadraticGraphData агрегирует TQuadraticEquation. Кратность см. пункт выше. Таблица 2
Примечания: 1. Класс TQuadraticSolution на диаграмме не имеет поля TQuadraticEquation* Equation. Это поле указано на самой связи агрегации. CASE-средства, такие как StarUML, автоматически добавляют это поле в код класса для реализации механизма связи при автоматической генерации кода. На изображении класса TQuadraticGraphData это поле добавлено вручную в секции атрибутов. При этом на самой связи оно не указано. Допускается использовать любой из вариантов. 2. Конструкторы выделены с помощью стереотипа «create», а деструкторы – «destroy». Это позволяет избежать добавления возвращаемого типа void для этих методов и не использовать для деструктора символ ~ (поскольку на диаграмме классов UML этот символ тоже используется). Рис 2. Диаграмма классов на втором шаге. Оценка модели: Данная модель тоже не лишена недостатков (например, класс, реализующий новый алгоритм решения квадратного уравнения будет иметь совершенно другой интерфейс, к которому придется адаптировать слой представления – нарушен принцип обратной совместимости, что затруднит развитие проекта), но следуя принципу YAGNI, сейчас мы их рассматривать не будем (хотя очень хочется). Следующий насущный вопрос: кто (или что) будет «связывать» уравнение с решением и с построением графика, а так же «следить» за ходом этих процессов? Перефразируя «умным языком», какой класс (объект) будет отвечать за обработку системных событий? Вывод: Необходимо включить в модель контроллер. Справка: Контроллер – структурный паттерн проектирования GRASP (англ. General Responsibility Assignment Software Patterns (общие образцы распределения обязанностей)) – это объект, который отвечает за обработку системных событий и не относится к интерфейсу пользователя. Контроллер определяет методы для выполнения системных операций. Шаг 3. «Позовите Вия!..» Итак, в системе для модуля-демонстратора определен прецедент «решить уравнение» (см. диаграмму прецедентов), состоящий из следующих сервисов: · Ввести коэффициенты · Вычислить корни · Построить график Очевидно, что прежде, чем ввести коэффициенты, объект «уравнение» необходимо создать (строго говоря, за создание экземпляров классов в GRASP отвечает другой паттерн – Creator, но для упрощения примера прикроем глаза на эту деталь). То же касается и экземпляров других классов. Список методов класса контроллера (назовем его, по традиции, TQuadraticController) будет примерно такой:
Таблица 3
Для экономии места и времени первая колонка с наименованием классов опущена. Рис. 3. Диаграмма классов на третьем шаге. Примечания: 1. Стереотип «controller» на изображении класса является ни к чему не обязывающим словом, указывающим лишь на то, что класс играет роль контроллера. Обозначение предназначено для более полного понимания учащимися назначения экземпляров класса в системе. StarUML игнорирует этот стереотип, генерируя каркас кода как для обычного класса. 2. Поскольку, как правило, объект типа «контроллер» в системе присутствует в единственном экземпляре (хотя, может быть несколько типов контроллеров), то он реализуется еще с использованием паттерна «одиночка» (singleton) . На данной диаграмме вместо значка «класс» можно также было использовать значок «объект», символизирующий то, что экземпляр класса данного назначения в системе один. 3. Класс «контроллер» связан с остальными ассоциациями без указания типа, что ничего не говорит об особенностях реализации данных связей. Это обозначение применено здесь для упрощения вида диаграммы. Оценка модели: На данном этапе развития системы модель с достаточной полнотой удовлетворяет принципам проектирования архитектуры. Каждый класс имеет достаточное сцепление (на текущем уровне знаний учащихся). Принцип единственности ответственности также выполняется (почти, не считая контроллера, но такова его судьба). Принцип минимального знания (закон Деметера) выполняется (на первый взгляд, ибо конкретной реализации нет, и проверить не на чем). Принцип DRY выполняется – ни один класс не повторяет функциональности другого. Принцип открытости/закрытости выполняется: для создания нового типа уравнения или нового алгоритма решения не нужно модифицировать существующие классы (кроме контроллера, но об этом ниже), а можно создать новые. Принцип обратной совместимости НЕ выполняется, поскольку реализация другого алгоритма решения потребует класса с другим интерфейсом, что, в свою очередь, повлечет за собой изменения в контроллере. Это также говорит о том, что связанность классов не достаточно низкая со всеми вытекающими последствиями. Но в данной ситуации вступает принцип YAGNI. Если заказчик ограничится только квадратными уравнениями, то данной модели будет вполне достаточно. Вывод: Есть о чем подумать и куда развиваться. Скорее всего, понадобиться еще один шаг для унификации интерфейсов (обобщения имеющихся понятий). Но это в следующем номере. Заключение В данном примере рассмотрена только модель одной части системы, а именно – модуля для демонстрации решения квадратных уравнений. Каждая следующая подсистема будет добавлять в модель свои классы. Например, подсистема контроля знаний может оперировать такими понятиями: «генератор коэффициентов уравнения» (создает коэффициенты для различного количества корней), «коллекция данных пользователя» (содержит результаты пользовательских вычислений), «проверяющий решения», «выставляющий оценку», «пользователь», «оценка» и т.п. Кроме того, в этой подсистеме появляется слой ORM, так как данные о пользователе хранятся в БД. В зависимости от выбранного типового (или не типового) решения реализации DOM, могут быть использованы классы «транзакция», «mapper», «dataset» и т.п.
|