Имена ролей, квалификаторы

Роль определяет одну сторону зависимости. В бинарной зависимости определены две роли. Имя роли однозначно определяет одну сторону зависимости. Роли дают возможность рассматривать бинарную зависимость как связь между объектом и множеством зависимых объектов: каждая роль является обозначением объекта или множества объектов, связанных зависимостью с объектом на другом конце зависимости. Имя роли можно рассматривать как производный атрибут, множеством значений которого является множество связанных с этой ролью объектов. В бинарной зависимости пара имён ролей может использоваться для идентификации этой зависимости.

На рисунке 2.11 имена начальник и сотрудник в зависимости руководит – это имена ролей; как уже было отмечено, эту зависимость удобнее назвать начальник-сотрудник. Ещё один пример имён ролей показан на рисунке 2.13.

Пользователь может быть либо владельцем, либо зарегистрированным пользователем директории; директория может содержать в себе другие директории.

 

Рис. 2.13. Имена ролей

 

Имена ролей следует обязательно указывать в тех случаях, когда зависимость устанавливается между объектами одного и того же класса (как в случаях, показанных на рисунках 2.11 и 2.13). Имена ролей должны быть уникальны, так как они используются для различения объектов, участвующих в зависимости.

Квалификатором называется некоторый атрибут, который позволяет снизить эффективную кратность зависимости. Квалификаторы применяются в зависимостях типов «один-ко-многим» или «много-ко-многим». Так, в примере, показанном на рисунке 2.14, использование квалификатора имя файла позволяет привести зависимость даёт доступ от вида, приведённого на рисунке 2.14а, к виду, показанному на рисунке 2.14б, сократив число зависимых объектов до одного. Ещё один пример использования квалификатора показан на рисунке 2.14в: использование квалификаторов и здесь позволяет сократить кратность зависимости до одного объекта. Квалификаторы указываются на схемах в прямоугольничках, пририсованных к прямоугольнику, изображающему соответствующий класс.

а)
б)
в)

Рис. 2.14. Использование квалификаторов

 

Рисунок 2.14в может быть проинтерпретирован следующим образом: центральныйкомпьютер + код ATM определяют конкретную ATM (отметим, что код АTM – имя одного из атрибутов класса ATM, а не класса центральный компьютер); аналогично центральный компьютер + код банка определяют конкретный компьютер банка. Использование квалификаторов повышает точность описания семантики и наглядность описания зависимостей.

 

Агрегация

Агрегация – это зависимость между классом составных объектов и классами, представляющими компоненты этих объектов (отношение «целое» – «часть»). Агрегация обозначается ромбиком: на рисунке 2.15 приведён пример агрегации; этот пример интерпретируется следующим образом: документ состоит из нескольких (нуля или более) абзацев; каждый абзац состоит из нескольких (нуля или более) предложений.

Рис. 2.15. Агрегация

Наиболее важным свойством отношения агрегации является его транзитивность (если A есть часть B, а B есть часть C, то A есть часть C): так, из рисунка 2.15 можно заключить, что документ состоит из нескольких (нуля или более) предложений. Легко видеть, что отношение агрегации антисимметрично (если A есть часть B, то B не есть часть A). Отметим также, что часть свойств целого может быть перенесена и на его части, возможно, с несущественными изменениями (например, контекст каждого оператора некоторой функции совпадает с внутренним контекстом всей функции).

Дальнейшие примеры агрегации показаны на рисунке 2.16. Отметим, что обе агрегации, показанные на рисунке 2.16а, следует рассматривать не как зависимости между пятёрками классов, а как четвёрки зависимостей между парами классов. Только при таком рассмотрении можно говорить о транзитивности и антисимметричности отношения агрегации.

 

б)
а)
в)

Рис. 2.16. Примеры агрегации

Обобщение и наследование

Обобщение и наследование позволяют выявить аналогии между различными классами объектов, определяют многоуровневую классификацию объектов. Так, в графических системах могут существовать классы, определяющие обрисовку различных геометрических фигур: точек, линий (прямых, дуг окружностей и кривых, определяемых сплайнами), многоугольников, кругов и т.п. (рис. 2.17).

 

Рис. 2.17. Обобщение (выделение суперклассов)

 

Обобщение позволяет выделить класс одномерные фигуры и считать классы прямая, дуга и сплайн подклассами класса одномерные фигуры, а класс одномерные фигуры – суперклассом классов прямая, дуга и сплайн.

Если при этом принять соглашение, что атрибуты и операции суперкласса действительны в каждом из его подклассов (говорят, что эти атрибуты и операции наследуются подклассами), то одинаковые атрибуты и операции классов прямая, дуга и сплайн (подклассов) могут быть вынесены в класс одномерные фигуры (суперкласс). Аналогично можно поступить и с двумерными фигурами, определив для классов многоугольник и круг суперкласс двумерная фигура. Наконец, можно определить класс фигура как суперкласс классов нульмерная фигура, одномерная фигура и двумерная фигура. Легко видеть, что отношения «подкласс – суперкласс» (обобщение) и «суперкласс – подкласс» (наследование) транзитивны, что позволяет строить классификационные деревья. При этом атрибуты и операции каждого суперкласса наследуются его подклассами всех уровней (мы как бы выносим за скобки одинаковые операции). Это значительно облегчает и сокращает описание классов.

На схемах обобщение (наследование) изображается треугольничком (рис. 2.17). Треугольничек следует ставить даже в том случае, когда суперкласс имеет всего один подкласс. Слово размерность, следующее за верхним треугольничком на рисунке 2.17, является дискриминатором.

Дискриминатор – это атрибут типа «перечисление», показывающий, по какому из свойств объектов сделано данное обобщение.

Другие примеры обобщения (наследования) показаны на рисунке 2.18 (эти примеры связаны с основным нашим примером – системой обслуживания клиентов банковским консорциумом).

 
 
а)


б)

Рис. 2.18. Другие примеры обобщения (наследования)

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

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

Иногда в подклассе бывает необходимо переопределить операцию, определённую в одном из его суперклассов. Для этого операция, которая может быть получена из суперкласса в результате наследования, определяется и в подклассе; это её повторное определение «заслоняет» её определение в суперклассе, так что в подклассе применяется не унаследованная, а переопределённая в нём операция. Напомним, что каждая операция определяется своей сигнатурой; следовательно, сигнатура переопределения операции должна совпадать с сигнатурой операции из суперкласса, которая переопределяется данной операцией. Так, в примере, изображённом на рисунке 2.17, в классе круг переопределяется операция «вращение» его суперкласса «фигура» (при повороте круга его изображение не меняется, что позволяет сделать операцию вращение в классе круг пустой).

Переопределение может преследовать одну из следующих целей:

− расширение: новая операция расширяет унаследованную операцию, учитывая влияние атрибутов подкласса;

− ограничение: новая операция ограничивается выполнением лишь части действий унаследованной операции, используя специфику объектов подкласса;

− оптимизация: использование специфики объектов подкласса позволяет упростить и ускорить соответствующий метод (например, переопределяя операцию max класса IntegerSet в его подклассе SortedIntegerSet, мы можем резко упростить её, используя специфические свойства упорядоченных множеств);

− удобство.

Целесообразно придерживаться следующих семантических правил наследования:

- все операции-запросы (операции, которые используют значения атрибутов, но не изменяют их) должны наследоваться всеми подклассами;

- все операции, изменяющие значения атрибутов, должны наследоваться во всех их расширениях;

- все операции, изменяющие значения ограниченных атрибутов, или атрибутов, определяющих зависимости, должны блокироваться во всех их расширениях (например, операция размер по оси x естественна для класса эллипс, но должна быть заблокирована (заменена пустой операцией) в его подклассе круг);

- операции не следует переопределять коренным образом; все методы, реализующие одну и ту же операцию, должны осуществлять сходное преобразование атрибутов;

- унаследованные операции можно уточнять, добавляя дополнительные действия.

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

Абстрактные классы

На рисунке 2.19 рассмотрена операция подсчёта выплат для различных категорий служащих фирмы: временных служащих с почасовой оплатой труда, постоянных служащих с понедельной оплатой труда и руководящих работников фирмы с помесячной оплатой.

Каждая из категорий служащих представлена своим подклассом класса служащий, от которого они наследуют атрибут годовой доход и операцию подсчёт выплат. Но подсчёт выплат для каждой категории служащих производится по-своему, с учётом значений их собственных (неунаследованных) атрибутов; поэтому в каждом из подклассов операция подсчёт выплат переопределяется. Следовательно, в суперклассе операция подсчёт выплат может быть определена произвольным образом, так как она никогда не будет выполняться. В то же время сигнатуры всех операций подсчёт выплат в суперклассе и в подклассах должны быть одинаковыми (иначе это будут разные операции). Из сказанного следует, что в суперклассе можно задать только сигнатуру операции подсчёт выплат, это обеспечит одинаковые сигнатуры этой операции во всех подклассах. Методы, реализующие операцию подсчёт выплат, достаточно определить только в подклассах класса служащий. Суперкласс, в котором заданы только атрибуты и сигнатуры операций, но не определены методы, реализующие его операции, называется абстрактным классом. Методы, реализующие операции абстрактного класса, определяются в его подклассах, которые называются конкретными классами.

 

 

Рис. 2.19. Абстрактный класс

 

Абстрактный класс не может иметь объектов, так как в нём не определены операции над объектами; объекты должны принадлежать конкретным подклассам абстрактного класса. Абстрактные классы используются для спецификации интерфейсов операций (методы, реализующие эти операции, впоследствии определяются в подклассах абстрактного класса). Абстрактные классы удобны на фазе анализа требований к системе, так как они позволяют выявить аналогию в различных, на первый взгляд, операциях, определённых в анализируемой системе.

 



lude $_SERVER["DOCUMENT_ROOT"]."/cgi-bin/footer.php"; ?>