Джим Арлоу, Айла Нейштадт - UML 2 и Унифицированный процесс. Практический объектно-ориентированный анализ и проектирование (1037782), страница 102
Текст из файла (страница 102)
25.15. Диаграмма классов системы электронной почты<<enumeration>>MessageType1 LegitimateUnclassified25.11. OCL на диаграммах других типов569jimsAddress:EmailAddressaddress = "jim@umlandtheunifiedprocess.com"Рис. 25.16. Адрес электронной почты jim@umlandtheunifiedprocess.comпредставлен как объект класса EmailAddressName() возвращает "Jim", EmailAddress::getDomain() возвращает "umlandtheunifiedprocess.com" и EmailAddress::getAddress() возвращает "jim@umlandtheunifiedprocess.com".
Объекты EmailAddress эквивалентны, если их атрибуты address (адрес) имеют одинаковое значение.Система для обработки пришедшей почты пользуется политикой белый/черный список:• Все почтовые сообщения, адрес отправителя (fromAddress) которыхвходит в черный список (blackList), удаляются.• Все почтовые сообщения, fromAddress которых входит в белый список (whiteList), помещаются в ящик входящей почты (inBox).• Все остальные почтовые сообщения помещаются в ящик для просмотра (reviewBox).• Состояние пришедшего сообщения (Message) меняется соответственно его типу: спам (удаляется), Legitimate (допустимый) или Unclassified (неклассифицированный).На рис. 25.17 показана диаграмма последовательностей для операцииMailSystem::classifyMessage( m : Message ). Диаграмма деятельностей дляэтой операции представлена в следующем разделе на рис.
25.18, и выдолжны заметить, как эти диаграммы соответствуют друг другу. Диаграмма последовательностей определяет, какие классы и операцииреализуют поведение, описанное диаграммой деятельностей.MailSystem (система электронной почты) – контекст выражения. OCLиспользовался для определения условий в комбинированном фрагменте alt.С помощью OCL также было задано состояние объектов на диаграмме(хотя это обычное применение OCL, поскольку OCL и UML имеют одинаковый синтаксис для описания состояний).25.11.2.
OCL на диаграммах деятельностиДиаграммы деятельности можно создавать для описания поведениялюбого элемента UMLмоделирования. На диаграммах деятельностейOCL используется для определения:• узлов вызова действия;• сторожевых условий переходов;• объектных узлов;• состояния объекта.570Глава 25. Введение в OCLsd ClassifyMessage:MailSystemm:Message:inBox:reviewBoxclassifyMessage( m : Message )setT ype( Unclassified )Unclassifiedalt[ blackList !>exists( a | a.getAddress() = m.fromAddress.getAddress() ) ]«delete»[ whiteList!>exists( a | a.getAddress() = m.fromAddressgetAddress() ) ]setT ype( Legitimate )Legitimateadd( m )[else]add( m )Рис. 25.17. Диаграмма последовательностей для операцииMailSystem::classifyMessage( m : Message )Например, на рис.
25.18 показана очень простая диаграмма деятельности для описания поведения ClassifyMailMessage (классифицироватьпочтовое сообщение) системы электронной почты, рассматриваемогов предыдущем разделе.Как видите, OCL используется для описания объектов, их состоянийи условий.OCL применяется для точного моделирования, а диаграммы деятельности по своей природе не могут быть точными. Кроме того, они частопредназначаются для заказчиков, которые не являются специалистами в технической области.
Поэтому целесообразность применения OCLна диаграммах этого типа довольно сомнительна. Например, ограничения на рис. 25.18 могли бы быть записаны просто на естественномязыке (английском или русском). Рассматривая возможность использования OCL на диаграммах деятельности, всегда необходимо учитывать назначение диаграммы и ее целевую аудиторию.25.11.3. OCL на диаграммах состоянийOCL используется в диаграммах состояний для определения:•сторожевых условий;57125.11. OCL на диаграммах других типовClassifyMailMessageGetMailMessagemessage[Unclassified][ blackList !>exists( a | a.getAddress() = m.fromAddress.getAddress() ) ]DeleteMessageelse[ whiteList !>exists( a | a.getAddress() = m.fromAddress.getAddress() ) ]ClassifyMessageAsLegitimateelsemessage[Unclassified]message[Legitimate]FileMessageForReviewFileMessageInInBoxРис.
25.18. Диаграмма деятельностей••••условий, налагаемых на состояния;целей действий;операций;значений параметров.Экземпляр контекста – это экземпляр классификатора, которому принадлежит конечный автомат. В качестве примера рассмотрим конечный автомат CheckingAccountActive (активный текущий счет) класса CheckingAccount (рис. 25.19).Из рисунка видно, что на диаграмме синтаксис OCL используется в сторожевых условиях. Чтобы конечный автомат был рабочим, необходимо учесть ограничения, которые записаны отдельно, чтобы не загромождать диаграмму.context CheckingAccount::balanceint:0context CheckingAccountinv:oclInState( InCredit ) implies ( balance >= 0 )572Глава 25. Введение в OCLCheckingAccountActiveActiveAcceptingWithdrawal[enoughMoney]withdraw( amount )OperatingHdeposit( amount )InCreditcloseopen[not inCredit]closed[inCredit]Overdrawn[notenoughMoney]withdraw( amount )RejectingWithdrawalРис.
25.19. Конечный автомат CheckingAccountActiveinv:oclInState( Overdrawn ) implies ( balance <0 )def:availableBalance = ( balance + overdraftLimit )def:enoughMoney = ( ( availableBalance – amount ) >= 0 )def:inCredit = ( balance >= 0 )Обратите внимание, как с помощью oclInState( InCredit ) организованассылка на состояние InCredit. В качестве альтернативы можно было быприкрепить инвариант (balance >= 0) прямо к состоянию InCredit в видепримечания.57325.12. Дополнительные вопросы25.12.
Дополнительные вопросыВ данном разделе рассматриваются некоторые не часто используемыеаспекты OCL:• навигация к и из классовассоциаций;• навигация по квалифицированным ассоциациям;• унаследованные ассоциации;• OCLMessage.25.12.1. Навигация к и от классовассоциацийИмя классаассоциации используется для навигации к классуассоциации.Классыассоциации обсуждаются в разделе 9.4.5. Осуществить навигацию к классуассоциации можно с помощью его имени. Для примерарассмотрим рис.
25.20.Операцию запроса getJobs() можно представить следующим образом:context Person::getJobs() : Set(Job)body:self.JobВыражение self.Job возвращает Set всех объектов Job, ассоциированныхс данным объектом Person. Этот Set можно использовать в OCLвыражениях. Пусть согласно бизнесправилу Person не может занимать двеодинаковые должности (Job). На OCL это можно описать так:context Personinv: человек не может занимать две одинаковые должностиself.Job>isUnique( j : Job | j.name )1..*Company0..*employerPersonemployeename : StringdateOfBirth : DategetAge() : IntegergetJobs()getTotalSalary() : RealJobname : Stringdescription : Stringsalary : RealgetEmployee() : PersongetEmployer() : CompanyРис.
25.20. Навигация к классу+ассоциации по имени574Глава 25. Введение в OCLПредположим, компания (Company) работает по схеме постепенного сокращения круга служебных обязанностей с приближением пенсионного возраста, и существует бизнесправило, запрещающее человеку(Person) старше 60 занимать более одной должности (Job). На OCL этоможно представить следующим образом:context Personinv: человек старше 60 может занимать только одну должность(self.getAge() >60) implies (self.Job>count() = 1)Чтобы получить общую заработную плату Person, необходимо сложитьего зарплату на каждой должности (Job):context Person::getTotalSalary() : Realbody: возвращаем суммарную заработную плату на всех должностяхself.Job.salary>sum()Навигацию от классаассоциации можно осуществлять как обычно –с помощью имен ролей на отношениях.
Например, вот OCL для операций getEmployee() и getEmployer() класса Job:context Job::getEmployee() : Personbody:self.employeecontext Job::getEmployer() : Companybody:self.employer25.12.2. Навигация по квалифицированным ассоциациямДля навигации по квалифицированной ассоциации после имени ролив квадратных скобках указываются квалификаторы.Квалифицированные ассоциации обсуждались в разделе 9.4.6. Длянавигации по квалифицированной ассоциации после имени ролив квадратных скобках размещается квалификатор (или разделенныйзапятыми список квалификаторов).На рис. 25.21 приведена простая модель клуба с различными уровнями членства. Предположим, существует бизнесправило, согласно которому членский ID 00001 всегда зарезервирован за председателемклуба.
На языке OCL это можно выразить следующим образом:context Clubinv: id председателя всегда 00001self.members['00001'].level = 'Chairman'57525.12. Дополнительные вопросыСlubclubName : StringclubAddress : Stringmemberld : String1club1membersMemberid : Stringname : Stringaddress : Stringlevel : StringРис. 25.21. Модель клуба с различными уровнями членстваЗдесь видно, как используется квалифицированная ассоциация с определенным значением квалификатора для выбора одного объекта измножества (Set) self.members.25.12.3. Унаследованные ассоциацииРассмотрим модель на рис. 25.22.
Она адаптирована из книги [Arlow 1]и представляет модель единиц измерений и систем единиц.Есть два разных типа единиц измерения (Unit): метрические (MetricUnits)и британские (ImperialUnits). MetricUnits принадлежат метрической системе (MetricSystem), а ImperialUnits – британской системе (ImperialSystem). Однако UMLмодель на рис. 25.22 не отражает этого различия.
По сути, вней определено, что любой Unit может принадлежать любой системеединиц (SystemOfUnits). Можно сделать модель более точной, если создать подкласс отношения между SystemOfUnits и Unit, как показано нарис. 25.23.Это решает проблему, но несколько загромождает диаграмму, и приувеличении числа подклассов Unit и SystemOfUnits ситуация быстро усугубляется.SystemOfUnitsImperialSystemsystemunits11..*UnitMetricUnitMetricSystemMeterCentimeterРис.
25.22. Модель единиц измерений и систем единицImperialUnitFootInch576Глава 25. Введение в OCLSystemOfUnitssystemunits11..*MetricSystem11..*MetricUnitMeterImperialSystemUnitCentimeter1ImperialUnit1..*FootInchРис. 25.23. В модель добавлен подкласс отношения между SystemOfUnits и UnitБолее изящный способ решения проблемы – определение OCLограничений следующим образом:context MetricUnitinv: относится к метрической системеself.system.oclIsTypeOf( MetricSystem )context ImperialUnitinv: относится к британской системеself.system.oclIsTypeOf( ImperialSystem )context MetricSysteminv: все единицы должны быть разновидностью MetricUnit или одним из его подклассовself.units–>forAll( unit | unit.oclIsKindOf( MetricUnit ) )context ImperialSysteminv: все единицы должны быть разновидностью ImperialUnit или одним из его подклассовОбратите внимание на использование OCLвыражений oclIsKindOf(...)и oclIsTypeOf(...):•oclIsKindOf(...) возвращает true, если тип объекта аналогичен типу,определенному параметром, или является одним из его подклассов.•oclIsTypeOf(...) возвращает true, если тип объекта точно такой же,как тип, заданный параметром.57725.12.