© Новичков А.Н., Interface Ltd., 2000
http://www.interface.ru/admail.asp?Url=/rational/ratline.htm
"Семь раз исправь, - десять отладь:" -
любимое занятие разработчика:
В прошлых частях мы достаточно много времени уделили такому мощному инструменту, как Rational Rose, причем старались разглядеть данный продукт глазами разработчика (постановщика). Достаточно подробно рассмотрели процесс проектирования как прямого, так и обратного на языке С++. Естественно, что не удалось покрыть полностью все возможности пакета, так как в этом случае пришлось бы писать целую книгу. К сожалению, во время написания третьей части данной статьи (такой же описательной как и все предыдущие) вышла в свет на русском языке великолепная книга по проектированию и кодогенерации ("UML и Rational Rose". У. Боггс, М. Боггс.). Стало быть 3 часть будет последней описательной частью:
Остальные части планируются уже не в описательном формате, а в проблематичном каждая статья берет за основу конкретную проблему проектирования или анализа, а также взаимодействия Rational Rose с другими продуктами компании Rational. Например, в одной из ближайших частей мы затронем интеграцию Rose с ClearCase и Requisite Pro. Также ждет своего череда статья по интеграции с Visual Studio.
Здесь мы уже не будем так дотошно проводить кодогенерацию, как в предыдущих частях. Основной упор будет поставлен на изобразительные возможности инструмента при моделировании, так как функция наследования (вещь сама по себе очень интересная) достаточно банальна сама по себе и не требует особого понимания и подхода. Простая вещь - простое действие. Давайте попробуем затронуть более сложные манипуляции с классами.
Давайте для начала вкратце проанализируем возможности Rational Rose в генерации и визуализации различных ассоциаций, связей и классов.
Для создания подобной связи Rose генерирует специальные атрибуты. Естественно, что при подобной ассоциации связь генерируется только для одного класса. При этом Rose генерирует закрытый атрибут (Private). В остальном генерация происходит так же как и в наследовании (связь показывается стрелкой Unidirectional Association). Ниже показана картинка отношения и код, сгенерированный Rose (далее в статье также будет использоваться такой же подход: описание связи, рисунок связи, сгенерированный файл заголовка).
Рис.1
ФАЙЛ NEWSTRING.H
//## begin module%1.3%.codegen_version preserve=yes // Read the documentation to learn more about C++ code generator // versioning. //## end module%1.3%.codegen_version //## begin module%39FD296801A9.cm preserve=no // %X% %Q% %Z% %W% //## end module%39FD296801A9.cm //## begin module%39FD296801A9.cp preserve=no //## end module%39FD296801A9.cp //## Module: NewString%39FD296801A9; Pseudo Package specification //## Source file: C:\Program Files\Rational2\Rose\C++\source\NewString.h #ifndef NewString_h #define NewString_h 1 //## begin module%39FD296801A9.additionalIncludes preserve=no //## end module%39FD296801A9.additionalIncludes //## begin module%39FD296801A9.includes preserve=yes //## end module%39FD296801A9.includes // String #include "String.h" //## begin module%39FD296801A9.additionalDeclarations preserve=yes //## end module%39FD296801A9.additionalDeclarations //## begin NewString%39FD296801A9.preface preserve=yes //## end NewString%39FD296801A9.preface //## Class: NewString%39FD296801A9 //## Category: <Top Level> //## Persistence: Transient //## Cardinality/Multiplicity: n class NewString { //## begin NewString%39FD296801A9.initialDeclarations preserve=yes //## end NewString%39FD296801A9.initialDeclarations public: //## Constructors (generated) NewString(); NewString(const NewString &right); //## Destructor (generated) ~NewString(); //## Assignment Operation (generated) NewString & operator=(const NewString &right); //## Equality Operations (generated) int operator==(const NewString &right) const; int operator!=(const NewString &right) const; //## Get and Set Operations for Associations (generated) //## Association: <unnamed>%39FD2A2602A7 //## Role: NewString::<the_String>%39FD2A270212 const String * get_the_String () const; void set_the_String (String * value); // Additional Public Declarations //## begin NewString%39FD296801A9.public preserve=yes //## end NewString%39FD296801A9.public protected: // Additional Protected Declarations //## begin NewString%39FD296801A9.protected preserve=yes //## end NewString%39FD296801A9.protected private: // Additional Private Declarations //## begin NewString%39FD296801A9.private preserve=yes //## end NewString%39FD296801A9.private private: //## implementation // Data Members for Associations //## Association: <unnamed>%39FD2A2602A7 //## begin NewString::<the_String>%39FD2A270212.role preserve=no public: String { -> RHN} String *the_String; //## end NewString::<the_String>%39FD2A270212.role // Additional Implementation Declarations //## begin NewString%39FD296801A9.implementation preserve=yes //## end NewString%39FD296801A9.implementation }; //## begin NewString%39FD296801A9.postscript preserve=yes //## end NewString%39FD296801A9.postscript // Class NewString //## Get and Set Operations for Associations (inline) inline const String * NewString::get_the_String () const { //## begin NewString::get_the_String%39FD2A270212.get preserve=no return the_String; //## end NewString::get_the_String%39FD2A270212.get } inline void NewString::set_the_String (String * value) { //## begin NewString::set_the_String%39FD2A270212.set preserve=no the_String = value; //## end NewString::set_the_String%39FD2A270212.set } //## begin module%39FD296801A9.epilog preserve=yes //## end module%39FD296801A9.epilog #endif
Это усложненный аналог предыдущей связи, во время исполнения которой генерируются ссылки на оба используемых класса. Визуально ассоциация показывается простой линией Association.
Рис.2
Ниже показаны только фрагменты двух классов, с упором на сгенерированную ассоциацию.
ФАЙЛ STRING.h
* * * private: //## implementation // Data Members for Associations //## Association: <unnamed>%39FD2E8E00AA //## begin String::<the_NewString>%39FD2E8E0399.role preserve=no public: NewString { -> RHN} NewString *the_NewString; //## end String::<the_NewString>%39FD2E8E0399.role // Additional Implementation Declarations //## begin String%39FD295103B9.implementation preserve=yes //## end String%39FD295103B9.implementation * * *
ФАЙЛ NEWSTRING.h
* * * private: //## implementation // Data Members for Associations //## Association: <unnamed>%39FD2E8E00AA //## begin NewString::<the_String>%39FD2E8E03A3.role preserve=no public: String { -> RHN} String *the_String; //## end NewString::<the_String>%39FD2E8E03A3.role // Additional Implementation Declarations //## begin NewString%39FD296801A9.implementation preserve=yes //## end NewString%39FD296801A9.implementation * * *
Данный вид ассоциаций также представляет особую ценность при проектировании классов. Множественность ассоциации задается в свойствах связи (пункты Role A Detail и Role B Detail. Где помимо ролей можно задать и специфические атрибуты конкретной ассоциациии, ну, скажем, дать ей название. Рис. 3 показывает скриншот данного окна). Рисунок 4, в свою очередь, демонстрирует внешний вид данной ассоциации.
Рис.3
Рис.4
ФАЙЛ STRING.h
* * * private: //## implementation // Data Members for Associations //## Association: <unnamed>%39FD300C00A9 //## begin String::<the_NewString>%39FD300C033E.role preserve=no public: NewString {n -> 1RHN} NewString *the_NewString; //## end String::<the_NewString>%39FD300C033E.role // Additional Implementation Declarations //## begin String%39FD295103B9.implementation preserve=yes //## end String%39FD295103B9.implementation * * *
ФАЙЛ NEWSTRING.h
* * * private: //## implementation // Data Members for Associations //## Association: <unnamed>%39FD300C00A9 //## begin NewString::<the_String>%39FD300C0348.role preserve=no public: String {1 -> nRHN} UnboundedSetByReference<String> the_String; //## end NewString::<the_String>%39FD300C0348.role // Additional Implementation Declarations //## begin NewString%39FD296801A9.implementation preserve=yes //## end NewString%39FD296801A9.implementation * * *
Как видно из фрагментов кода, мы получили, как и впредыдущих случаях, ссылку на класс, в то то время, как для другого класса был использован контейнер. По умолчанию Rational Rose не предоставляет реализацию контейнерного класса. Вместо него генерируется код с UnboundedSetByReference в качестве контейнерного. Указание же конкретного контейнерного класса - дело самого разработчика.
Данный пример практически аналогичен предыдущему, с той лишь разницей, что, генерируется двойная связь: контейнерные классы на обоих концах.
Данный вид ассоциаций представляет логическое продолжение предыдущего типа ассоциаций, но в отличие от него имеет строго ограниченное количество связей (ограничение по диапазону). Рисунок 5 показывает модель подобного взаимоотношения классов
Рис.5
Код получается следующим:
ФАЙЛ STRING.h
//## begin module%1.3%.codegen_version preserve=yes // Read the documentation to learn more about C++ code generator // versioning. //## end module%1.3%.codegen_version //## begin module%39FD295103B9.cm preserve=no // %X% %Q% %Z% %W% //## end module%39FD295103B9.cm //## begin module%39FD295103B9.cp preserve=no //## end module%39FD295103B9.cp //## Module: String%39FD295103B9; Pseudo Package specification //## Source file: C:\Program Files\Rational2\Rose\C++\source\String.h #ifndef String_h #define String_h 1 //## begin module%39FD295103B9.additionalIncludes preserve=no //## end module%39FD295103B9.additionalIncludes //## begin module%39FD295103B9.includes preserve=yes //## end module%39FD295103B9.includes // NewString #include "NewString.h" //## begin module%39FD295103B9.additionalDeclarations preserve=yes //## end module%39FD295103B9.additionalDeclarations //## begin String%39FD295103B9.preface preserve=yes //## end String%39FD295103B9.preface //## Class: String%39FD295103B9 //## Category: <Top Level> //## Persistence: Transient //## Cardinality/Multiplicity: 1 class String { //## begin String%39FD295103B9.initialDeclarations preserve=yes //## end String%39FD295103B9.initialDeclarations public: //## Constructors (generated) String(); String(const String &right); //## Destructor (generated) ~String(); //## Assignment Operation (generated) String & operator=(const String &right); //## Equality Operations (generated) int operator==(const String &right) const; int operator!=(const String &right) const; //## Get and Set Operations for Associations (generated) //## Association: <unnamed>%39FD300C00A9 //## Role: String::<the_NewString>%39FD300C033E const NewString * get_the_NewString () const; void set_the_NewString (NewString * value); // Additional Public Declarations //## begin String%39FD295103B9.public preserve=yes //## end String%39FD295103B9.public protected: // Additional Protected Declarations //## begin String%39FD295103B9.protected preserve=yes //## end String%39FD295103B9.protected private: // Additional Private Declarations //## begin String%39FD295103B9.private preserve=yes //## end String%39FD295103B9.private private: //## implementation // Data Members for Associations //## Association: <unnamed>%39FD300C00A9 //## begin String::<the_NewString>%39FD300C033E.role preserve=no public: NewString {n -> 1RHN} NewString *the_NewString; //## end String::<the_NewString>%39FD300C033E.role // Additional Implementation Declarations //## begin String%39FD295103B9.implementation preserve=yes //## end String%39FD295103B9.implementation }; //## begin String%39FD295103B9.postscript preserve=yes //## end String%39FD295103B9.postscript // Class String //## Get and Set Operations for Associations (inline) inline const NewString * String::get_the_NewString () const { //## begin String::get_the_NewString%39FD300C033E.get preserve=no return the_NewString; //## end String::get_the_NewString%39FD300C033E.get } inline void String::set_the_NewString (NewString * value) { //## begin String::set_the_NewString%39FD300C033E.set preserve=no the_NewString = value; //## end String::set_the_NewString%39FD300C033E.set } //## begin module%39FD295103B9.epilog preserve=yes //## end module%39FD295103B9.epilog #endif
ФАЙЛ NEWSTRING.h
//## begin module%1.3%.codegen_version preserve=yes // Read the documentation to learn more about C++ code generator // versioning. //## end module%1.3%.codegen_version //## begin module%39FD296801A9.cm preserve=no // %X% %Q% %Z% %W% //## end module%39FD296801A9.cm //## begin module%39FD296801A9.cp preserve=no //## end module%39FD296801A9.cp //## Module: NewString%39FD296801A9; Pseudo Package specification //## Source file: C:\Program Files\Rational2\Rose\C++\source\NewString.h #ifndef NewString_h #define NewString_h 1 //## begin module%39FD296801A9.additionalIncludes preserve=no //## end module%39FD296801A9.additionalIncludes //## begin module%39FD296801A9.includes preserve=yes //## end module%39FD296801A9.includes // String #include "String.h" //## begin module%39FD296801A9.additionalDeclarations preserve=yes //## end module%39FD296801A9.additionalDeclarations //## begin NewString%39FD296801A9.preface preserve=yes //## end NewString%39FD296801A9.preface //## Class: NewString%39FD296801A9 //## Category: <Top Level> //## Persistence: Transient //## Cardinality/Multiplicity: n class NewString { //## begin NewString%39FD296801A9.initialDeclarations preserve=yes //## end NewString%39FD296801A9.initialDeclarations public: //## Constructors (generated) NewString(); NewString(const NewString &right); //## Destructor (generated) ~NewString(); //## Assignment Operation (generated) NewString & operator=(const NewString &right); //## Equality Operations (generated) int operator==(const NewString &right) const; int operator!=(const NewString &right) const; //## Get and Set Operations for Associations (generated) //## Association: <unnamed>%39FD300C00A9 //## Role: NewString::<the_String>%39FD300C0348 const UnboundedSetByReference<String> get_the_String () const; void set_the_String (UnboundedSetByReference<String> value); // Additional Public Declarations //## begin NewString%39FD296801A9.public preserve=yes //## end NewString%39FD296801A9.public protected: // Additional Protected Declarations //## begin NewString%39FD296801A9.protected preserve=yes //## end NewString%39FD296801A9.protected private: // Additional Private Declarations //## begin NewString%39FD296801A9.private preserve=yes //## end NewString%39FD296801A9.private private: //## implementation // Data Members for Associations //## Association: <unnamed>%39FD300C00A9 //## begin NewString::<the_String>%39FD300C0348.role preserve=no public: String {1 -> nRHN} UnboundedSetByReference<String> the_String; //## end NewString::<the_String>%39FD300C0348.role // Additional Implementation Declarations //## begin NewString%39FD296801A9.implementation preserve=yes //## end NewString%39FD296801A9.implementation }; //## begin NewString%39FD296801A9.postscript preserve=yes //## end NewString%39FD296801A9.postscript // Class NewString //## Get and Set Operations for Associations (inline) inline const UnboundedSetByReference<String> New-String::get_the_String () const { //## begin NewString::get_the_String%39FD300C0348.get preserve=no return the_String; //## end NewString::get_the_String%39FD300C0348.get } inline void NewString::set_the_String (UnboundedSetByReference<String> value) { //## begin NewString::set_the_String%39FD300C0348.set preserve=no the_String = value; //## end NewString::set_the_String%39FD300C0348.set } //## begin module%39FD296801A9.epilog preserve=yes //## end module%39FD296801A9.epilog #endif
Код специально приведен полностью, чтобы можно было бы оценить размах Rose в плане генерации сложных связей.
Лаконичным завершение данного вида связи можно считать связь один к:. В нашем случае 1 к 6
Рис.6
При этом код будет выглядеть следующим образом
ФАЙЛ NEWSTRING.h
* * * private: //## implementation // Data Members for Associations //## Association: <unnamed>%39FD4614028A //## begin NewString::<the_String>%39FD461502A0.role preserve=no public: String {1 -> 6RHN} String *the_String[6]; //## end NewString::<the_String>%39FD461502A0.role // Additional Implementation Declarations //## begin NewString%39FD296801A9.implementation preserve=yes //## end NewString%39FD296801A9.implementation * * *
Таким же образом можно сгенерировать любой вид связи. Скажем, разработчику ничего не мешает определить связь 6 к 6 или 10 к 10. Код, сгенерированный системой, при этом будет выглядеть как "положено":
При кодогенерации следует учитывать одно "но". Все что происходит по умолчанию не всегда может устроить конкретного раработчика. Даже учитывая то, что инструмент изначально настроен на генерацию правильного и качественного кода, все равно Rose требует дополнительной подстройки под конкретный проект (релиз.. и пр.)
Мощные выразительные свойства в Rational Rose сочетаются с гибкой системой подсказок и настроек. Так, например, Rose позволяет проводить не только простую настройку инструментальной панели (Это возможность стала давно тривиальной, и рассматривать ее как нечто выдающееся нет необходимости), но и что очень важно - настраивать по своему усмотрению внешний вид диаграмм, для которых отсутствует картинка.
Внутренне устроение Rose позволяет менять системный файл "defaultstereotypes.ini", в котором содержатся наименования предопределенных стереотипов со ссылками на соответствующие картинки. Так если пользователя не устраивает картинка, то он спокойно может заме-нить ссылку на свой графический файла. Правда при этом следует учитывать, что все рисунки должны храниться только в формате WMF, и иметь длину не более 512 байт.
В следующей части статьи мы рассмотрим
См. также: