UML2 и унифицированный процесс. Джим арлоуайла нейштадтпрактический объектно ориентированныйанализ и проектированиеu
![]()
|
Рис. 9.23. Типы зависимостей поставщик стереотип часто опускается клиент «use» A B doSomething( ) bar( ):B foo(b:B) Рис. 9.24. Два класса с отношением зависимости типа «use» 222 Глава 9. Отношения 3. Операция класса A где то в своей реализации использует объект класса B, но не в качестве атрибута. Варианты 1 и 2 довольно просты, а вот вариант 3 представляет больший интерес. Такая ситуация возможна, если одна из операций класса А создала временный объект класса В. Ниже приводится фрагмент Java кода для этого случая: class A { void doSomething() { B myB = new B(); // используем myB некоторым образом } } Хотя одна зависимость «use» может использоваться как универсальная для всех трех перечисленных случаев, есть и другие, более специали зированные стереотипы зависимостей, которые можно было бы приме нить. Ситуации 1 и 2 можно более точно смоделировать с помощью зависи мости «parameter», а ситуацию 3 – с помощью зависимости «call». Однако от UML модели не часто требуется такой уровень детализации, и боль шинство разработчиков моделей считают, что намного понятней и про ще просто устанавливать между соответствующими классами зависи мость «use», как показано выше. 9.5.1.2. Зависимость «call» Зависимость «call» (вызов) устанавливается между операциями – опе рация клиент вызывает операцию поставщик. Этот тип зависимости в UML моделировании используется не очень широко. Он применяет ся на уровне детализации, более глубоком, чем тот, на который боль шинство разработчиков готовы пойти. Кроме того, в настоящее время очень немногие средства моделирования поддерживают зависимости между операциями. 9.5.1.3. Зависимость «parameter» Поставщик является параметром операции клиента. 9.5.1.4. Зависимость «send» Клиент – это операция, посылающая поставщика (который должен быть сигналом) в некоторую неопределенную цель. Сигналы рассматривают ся в разделе 15.6, а пока будем представлять их как особые типы клас сов, используемые для передачи данных между клиентом и целью. 9.5. Что такое зависимость? 223 9.5.1.5. Зависимость «instantiate» Клиент – это экземпляр поставщика. 9.5.2. Зависимости абстракции Зависимости абстракции моделируют зависимости между сущностя ми, находящимися на разных уровнях абстракции. В качестве приме ра можно привести класс аналитической модели и тот же класс в про ектной модели. Существует четыре зависимости абстракции: «trace», «substitute», «refine» и «derive». 9.5.2.1. Зависимость «trace» Зависимость «trace» часто используется, чтобы проиллюстрировать от ношение, в котором поставщик и клиент представляют одно понятие, но находятся в разных моделях. Например, поставщик и клиент могут находиться на разных стадиях разработки. Поставщик мог бы быть аналитическим представлением класса, а клиент – более детальным проектным представлением. Также «trace» можно использовать, чтобы показать отношение между функциональным требованием, таким как «банкомат должен обеспечивать возможность снятия наличных денег вплоть до достижения кредитного лимита карты», и прецедентом, поддерживающим это требование. 9.5.2.2. Зависимость «substitute» Зависимость «substitute» (заместить) показывает, что клиент во время выполнения может заменять поставщика. Замещаемость основывает ся на общности контрактов и интерфейсов клиента и поставщика, т. е. они должны предоставлять один и тот же набор сервисов. Обратите внимание, что замещаемость не достигается посредством отношений специализации/обобщения между клиентом и поставщиком (специа лизация/обобщение обсуждаются в разделе 10.2). В сущности, «substi tute» специально разработана для использования в средах, не поддер живающих специализации/обобщения. 9.5.2.3. Зависимость «refine» Тогда как зависимость «trace» устанавливается между элементами раз ных моделей, «refine» (уточнить) может использоваться между элемен тами одной и той же модели. Например, в модели может быть две вер сии класса, одна из которых оптимизирована по производительности. Поскольку оптимизация производительности является разновидно стью уточнения, это отношение между двумя классами можно смоде лировать как зависимость «refine» с примечанием, описывающим суть уточнения. 224 Глава 9. Отношения 9.5.2.4. Зависимость «derive» Стереотип «derive» (получить) используется, когда необходимо явно по казать возможность получения одной сущности как производной от другой. Например, в имеющемся классе BankAccount есть список Tran saction (транзакция), в котором каждая Transaction содержит Quantity (количество) денег. По требованию всегда можно вычислить текущий баланс, суммируя Quantity по всем Transaction. Существует три способа показать, что balance (баланс) счета (его Quantity) может быть производ ной сущностью. Они приведены в табл. 9.3. Таблица 9.3 Все перечисленные способы обозначения балансов как производных сущностей эквивалентны, хотя первая модель в табл. 9.3 является наиболее явной. Мы предпочитаем явные модели. 9.5.3. Зависимости доступа Зависимости доступа выражают способность доступа одной сущности к другой. Существует три зависимости доступа: « access», «import» и « permit». 9.5.3.1. Зависимость «access» Зависимость « access» (доступ) устанавливается между пакетами. В UML пакеты используются для группировки сущностей. Самое главное Модель Описание Класс BankAccount имеет «derive» ассо циацию с Quantity, где Quantity играет роль баланса банковского счета. Эта модель подчеркивает, что balance является производным от коллек ции Transaction класса BankAccount. В данном случае в имени роли ис пользуется слэш, чтобы показать, что между BankAccount и Quantity уста новлено отношение « derive». Такое обозначение менее явное, по скольку не показывает, производ ным чего является balance. Здесь balance показан как производ ный атрибут, что обозначено слэ шем, предваряющим имя атрибута. Это самое краткое выражение зави симости « derive». Transaction Quantity BankAccount 0..* 1 1 1 balance 1 «derive» 1 Transaction Quantity BankAccount 0..* 1 1 1 /balance 1 1 Transaction Quantity BankAccount /balance:Quantity 0..* 1 1 1 9.6. Что мы узнали 225 здесь то, что « access» разрешает одному пакету доступ ко всему откры тому содержимому другого пакета. Однако каждый пакет определяет пространство имен, и с установлением отношения « access» пространст ва имен остаются изолированными. Это означает, что элементы кли ентского пакета должны использовать имена путей (pathnames), когда хотят обратиться к элементам пакета поставщика. Более подробное обсуждение данного вопроса представлено в глОднако иве 11. 9.5.3.2. Зависимость «import» Зависимость « import» концептуально аналогична «access», за исключе нием того, что пространство имен поставщика объединяется с про странством имен клиента. Это обеспечивает возможность элементам клиента организовывать доступ к элементам поставщика без необхо димости указывать в именах элементов имя пакета. Однако иногда это может приводить к конфликтам имен, если имена элемента клиента и элемента поставщика совпадают. Очевидно, что в этом случае необ ходимо использовать полные имена. В главе 11 этот вопрос обсуждает ся более подробно. 9.5.3.3. Зависимость «permit» Зависимость « permit» (разрешить) обеспечивает возможность управля емого нарушения инкапсуляции, но в целом этого отношения следует избегать. Клиентский элемент имеет доступ к элементу поставщику не зависимо от объявленной видимости последнего. Часто зависимость « permit» устанавливается между двумя родственными классами, когда клиентскому классу выгодно (вероятно, по причинам производительно сти) иметь доступ к закрытым членам поставщика. Не все языки про граммирования поддерживают зависимости « permit». C++ позволяет классу объявлять друзей, которые имеют разрешение на доступ к его закрытым членам. Но эта возможность была (и, наверное, благоразум но) изъята из Java и C#. 9.6. Что мы узнали В этой главе мы начали обсуждение отношений, которые связывают воедино модели UML. Мы узнали следующее: • Отношения – это семантические соединения между сущностями. • Соединения между объектами называются связями. • Связь возникает, когда объект сохраняет объектную ссылку на другой объект. • Объекты реализуют поведение системы через взаимодействие: • взаимодействие возникает, когда объекты обмениваются со общениями по связям; • когда объект получает сообщение, он выполняет соответству ющую операцию. 226 Глава 9. Отношения • Разные OO языки программирования реализуют связи по раз ному. • Диаграммы объектов отображают объекты и их связи в определен ный момент времени. • Диаграммы – это моментальные снимки исполняющейся ОО системы в определенный момент времени. • Объекты могут играть определенные роли по отношению друг к другу; роль, исполняемая объектом в связи, определяет семан тику его участия во взаимодействии. • N арные связи могут объединять более двух объектов; они ото бражаются в виде ромба, соединенного линиями с каждым объ ектом, но используются редко. • Пути – это линии, соединяющие элементы модели UML: • прямоугольный стиль – прямые линии, располагающиеся под прямым углом; • наклонный стиль – косые линии; • криволинейный стиль – кривые линии; • необходимо следовать и придерживаться одного стиля, если только сочетание стилей не повышает читаемость диаграммы (что обычно не наблюдается). • Ассоциации – это семантические соединения между классами. • Если между двумя объектами есть связь, между классами этих объектов должна существовать ассоциация. • Связи – это экземпляры ассоциаций, так же как объекты – эк земпляры классов. • Ассоциации могут иметь (не обязательно) следующие элементы: • Имя ассоциации: • перед или после него может стоять маленькая черная стрел ка, обозначающая направление чтения этого имени; • должно быть глаголом или глагольной группой; • записывается в стиле lowerCamelCase; • использует или имя ассоциации, или имена ролей, но не одновременно и то, и другое. • Имена ролей на одном или обоих концах ассоциации: • должны быть существительным или именной группой, описывающими семантику роли; • записывается в стиле lowerCamelCase. • Кратность: • показывает количество объектов, которые могут участво вать в отношении в любой момент времени; 9.6. Что мы узнали 227 • объекты могут появляться и исчезать, но кратность огра ничивает число объектов, принимающих участие в отно шении в любой момент времени; • кратность задается разделенным запятыми списком ин тервалов, например 0..1, 3..5; • не существует применяемой по умолчанию кратности – ес ли кратность не обозначена явно, значит, она не определена. • Возможность навигации: • обозначается стрелкой на конце отношения – если на отно шении стрелки не обозначены, значит, оно двунаправлен ное; • возможность навигации показывает, что отношение мож но пройти в направлении по стрелке; • вероятно, используя другой путь, можно пройти в обрат ном направлении, но это потребует больших вычислитель ных затрат. • Ассоциация между двумя классами эквивалентна наличию у од ного класса псевдоатрибута, который может хранить ссылку на объект другого класса: • часто ассоциации и атрибуты взаимозаменяемы; • ассоциации следует применять, если на одном конце ассоциа ции находится важный класс и это необходимо подчеркнуть; • атрибуты используются, когда класс, участвующий в отноше нии, не очень важен (например, библиотечные классы, такие как String или Date). • Класс ассоциация – это ассоциация, которая является еще и классом: • может иметь атрибуты, операции и отношения; • класс ассоциация может использоваться, когда между парой объектов в любой момент времени существует только одна единственная связь; • если пара объектов в определенный момент времени может иметь много связей друг с другом, тогда мы материализуем отношение, заменяя его обычным классом. • Квалифицированные ассоциации используют квалификатор, чтобы выбрать из целевого набора один объект: • квалификатор должен быть уникальным ключом для целево го набора; • квалифицированные ассоциации понижают кратность отно шения n ко многим до n к одному; • они являются полезным способом обратить внимание на уни кальные идентификаторы. 228 Глава 9. Отношения • Зависимости – это отношения, в которых изменение поставщика оказывает влияние или поставляет информацию клиенту. • Клиент некоторым образом зависит от поставщика. • Зависимости изображаются в виде пунктирной линии со стрел кой, направленной от клиента к поставщику. • Зависимости использования: • «use» – клиент некоторым образом использует поставщика – универсальная зависимость; • «call» – операция клиента инициирует операцию поставщика; • «parameter» – поставщик является параметром или возвращае мым значением одной из операций клиента; • «send» – клиент отправляет поставщика (который должен быть сигналом) в заданную цель; • «instantiate» – клиент является экземпляром поставщика. • Зависимости абстракции: • «trace» – клиент является историческим развитием поставщика; • «substitute» – клиент может замещать поставщика во время выполнения; • «refine» – клиент является версией поставщика; • «derive» – клиент может быть производным от поставщика: • производные отношения могут быть показаны явно с по мощью зависимости «derive»; • производные отношения могут быть показаны путем до бавления слэша перед именем роли или отношения; • производные атрибуты можно обозначить путем добавле ния слэша перед именем атрибута. • Зависимости доступа: • « access» – зависимость между пакетами, когда клиентский пакет имеет доступ ко всему открытому содержимому пакета поставщика; пространства имен пакетов остаются изолиро ванными; • « import» – зависимость между пакетами, когда клиентский пакет имеет доступ ко всему открытому содержимому пакета поставщика; пространства имен пакетов объединяются; • « permit» – управляемое нарушение инкапсуляции, когда кли ент имеет доступ к закрытым членам поставщика; эта зависи мость не поддерживается широко, ее следует избегать по мере возможности. 10 Наследование и полиморфизм 10.1. План главы В этой главе основное внимание уделено ключевым концепциям на следования (раздел 10.3) и полиморфизма (раздел 10.4). Но прежде чем углубиться в эти вопросы, важно понять принцип обобщения, об суждаемый в разделе 10.2. 10.2. Обобщение Обобщение – это отношение между более общей сущностью и более специальной сущностью. Перед тем как мы сможем перейти к обсуждению наследования и по лиморфизма, необходимо сформировать четкое представление об идее обобщения. Обобщение – это отношение между более общим элементом и более специальным элементом, когда более специальный элемент полностью совместим с более общим элементом, но содержит боль шее количество информации. Два элемента подчиняются принципу замещаемости: более специаль ный элемент может использоваться везде, где предполагается исполь зование более общего элемента, без нарушения системы. Очевидно, что обобщение – намного более прочный тип отношений, чем ассоциация. В самом деле, обобщение подразумевает самый высокий уровень зави симости (и, следовательно, связанности) между двумя элементами. 10.2.1. Обобщение классов С концептуальной точки зрения идея обобщения проста. Всем хорошо известны общие сущности, например дерево, и более специальные сущ ности, например дуб, который является конкретным типом дерева. 230 Глава 10. Наследование и полиморфизм 10.6. Что мы узнали 10.2. Обобщение 10.2.1. Обобщение классов 10.3. Наследование классов 10.3.1. Переопределение 10.3.2. Абстрактные операции и классы 10.3.3. Уровень абстракции 10.3.4. Множественное наследование 10.4. Полиморфизм подробно рассматриваем полиморфизм изучаем обобщение классов подробно рассматриваем наследование 10.4.1. Пример полиморфизма 10.5. Дополнительные аспекты обобщения 10.5.1. Множества обобщения 10.5.2. Множества всех типов Рис. 10.1. План главы 10.3. Наследование классов 231 Обобщение применяется ко всем классификаторам. В этой книге нам уже встречалось обобщение, применяемое к прецедентам и актерам. Теперь обсудим его применение к классам. На рис. 10.2 представлен класс Shape (фигура); это, несомненно, очень общее понятие! От него происходят дочерние классы, подклассы, по томки, наследники (все эти термины широко используются), которые являются более специальными разновидностями общего понятия Shape. Согласно принципу замещаемости экземпляр любого из этих подклас сов может использоваться везде, где предполагается экземпляр супер класса (или «надкласса») Shape. Иерархия обобщения создается путем обобщения более специальных сущностей и специализации более общих сущностей. При подробном обсуждении атрибутов и операций этих классов мы увидим, что прийти к приведенной выше иерархии можно двумя спо собами: либо через процесс специализации, либо через процесс обоб щения. В специализации при анализе сначала определяется общая концепция Shape, а затем происходит ее специализация до конкретных типов фигур. В обобщении анализ выявляет более специализирован ные Square (квадрат), Circle (круг) и Triangle (треугольник), а затем уста навливается, что все они имеют общие характеристики, которые мож но выделить в более общий надкласс. ОО анализ стремится использовать и специализацию, и обобщение од новременно. Хотя из собственного опыта можем сказать, что в процес се анализа следует научиться как можно раньше замечать более общие вещи. |