“Долго запрягается, но быстро едет…” –
стиль жизни отечественного разработчика…
Данной статьей мне хочется открыть ретроспективу материалов по Rational Rose. Долгое время у меня вызывало сомнение необходимость подобного эссе, поскольку про Розу сказано достаточно много во всех периодических изданиях. Аналитики и проектировщики имеют представление о данном продукте и приблизительно знают, к чему его применить. Проблема только в разработчиках, которые либо не до конца представляют возможности пакета, либо имеют неправильную установку. Сия установка (комплекс) получается в результате того, что сам инструмент Rational Rose – широкопрофильный, покрывающий все большую часть аналитиков, проектировщиков и все тех же разработчиков. А по традиции его используют активно только первые две категории работников, поскольку для них очевидна необходимость применения CASE средств для описания как модели предприятия, так и бизнес-процессов и пр…
О том, что может дать разработчикам собственно сама Роза, мы поговорим на страницах статьи, сейчас же необходимо отметить, что основное ее предназначение - это проектирование классов и реинжениринг уже существующих…
Вот мы плавно и подошли к камню преткновения… Ни для кого не секрет, что разработчик, профессионально владеющий инструментом (языком программирования, оболочкой, и пр.), очень неохотно использует новые инструменты, не говоря о методологиях. В этом случае пушистый котеночек превращается в ежа с большим количеством острых иголок, направленных на отстаивание собственной позиции (по иголке на каждую!). В первую очередь, поэтому все новое так трудно приживается на нашей российской ниве.
Вернемся от общих размышлений к продукту. В разработческих кругах основательно засело мнение о том, что: 1) генерация программ невозможна в принципе (по слухам, Роза как раз-таки и генерирует программы), 2) генераторы никогда не смогут генерировать эффективный код!!! Ну что можно сказать по такому поводу?! Конечно же, часть возражений можно принять в расчет, но в основном это не так!
Начнем развеивать мифы:
Данная статья является моей попыткой продемонстрировать Rational Rose именно с точки зрения разработчика. Подобная попытка уже была предпринята мною несколько ранее в статьях по конфигурационному управлению (Clear Case), по средствам тестирования (Rational Purify, Quantify, PureCoverage). Подобная очередность не случайна, поскольку их назначение для меня, как для разработчика, очевидно и не требует дополнительного пояснения, чего не скажешь про Rational Rose. Здесь кавалерийский наскок не получился! Пришлось разбираться продукте и вникать во все детали. Плодом разбирательств и является данная статья. В ней я попытаюсь объяснить, зачем Rose нужен разработчикам.
По своей структуре статья - это “ужастик” с продолжением!!! Сейчас мне трудно сказать, сколько будет в ней частей… но никак не меньше 3. А вообще, я призываю всех разработчиков, прочитавших первую часть, высказать свое мнение о ней и дать рекомендации по дальнейшему “продвижению”!!! Данная информация от Вас мне очень и очень важна, просто не хочется писать о том, что не вызывает интереса!!!
Итак, ниже вам предложена первая, очень общая статья в стиле доклада. В ней приводятся только общие моменты с конкретными примерами на С++.
Приятного чтения!
Компания Rational Software является лидирующей в области создания методологий и программных решений, ориентированных на программистов, аналитиков, тестировщиков. Спектр выпускаемого программного обеспечения целиком покрывает потребность всех участников проекта: от аналитиков до разработчиков и внедренцев. Все программно-методологические решения - плод многолетнего труда аналитиков и разработчиков как самой Rational, так и ее партнеров. В итоге все решения были собраны воедино. Так появился RUP – Rational Unified Process – методологическая энциклопедия, в которой описаны все шаги, необходимые для создания качественного программного продукта. Пользуясь подобной энциклопедией и применяя соответствующие инструменты, рекомендуемые Rational, команда будет создавать обеспечение качественно и в срок. “Строй быстрей и качественней!” – вот лозунг, выдвигаемый Rational.
Особое место в RUP занимают проектирование и конфигурационное управление. Особо выделяются они потому, что те два инструмента, которые поддерживаются на данных этапах (Rational Rose и Rational ClearCase), используются на протяжении всего жизненного цикла разработки программного обеспечения. Если Rose со своей стороны используют и проектировщики, и разработчики, и аналитики (практически добрая половина коллектива), то ClearCase используют все, поскольку результатом любой деятельности в мире информационных технологий является файл, который где-то необходимо хранить и не просто хранить, а знать все изменения, которые были в него внесены на каждом этапе разработки информационной системы.
Rational Rose, являясь объектно-ориентированым средством проектирования, способна моделировать ситуации любой сложности: от проектирования банковской системы до разработки кода на С++. В умелых руках Rose неоценимый инструмент!
Rational Rose в отличие от подобных средств проектирования способна проектировать системы любой сложности, то есть инструментарий программы допускает как высокоуровневое (абстрактное) представление (например, схема автоматизации предприятия), так и низкоуровневое проектирование (интерфейс программы, схема базы данных, частичное описание классов). Вся мощь программы базируется всего на 7 диаграммах, которые в зависимости от ситуации способны описывать различные действия.
Давайте попробуем разобраться, что же реально даст данный инструмент отдельным участникам проекта: проектировщикам, аналитикам, разработчикам.
Проектировщикам. В большинстве случаев подрядчик не может написать качественное программное обеспечение в установленный срок, поскольку заранее не были оговорены с заказчиком наиболее важные моменты в системе. Например, заказчик забыл рассказать об очень важной составляющей своей системы, а вспомнил об этом, когда увидел (запустил) предложенную готовую программу. Соответственно, проблема на данном этапе – взаимопонимание двух сторон. Значит, если проектировщик с заказчиком посредством моделирования предметной области в Rational Rose четко и скрупулезно описали каждую деталь и увидели ее в виде понятных диаграмм, то проблему взаимопонимания можно отбросить. Таким образом, при разработке современных информационных систем много времени уделяется проектированию (моделированию предметной области), поскольку необходимо изначально договориться с заказчиком обо всех нюансах, а не вносить в режиме “пожарной команды” изменения на более поздних этапах. То есть Rational Rose поможет на концептуальном уровне разобраться с генеральным планом автоматизации. Для улучшения взаимопонимания обеих сторон совместно с Rose применяют инструмент SoDA, позволяющий на основе построенной модели дать полный отчет по ее состоянию, соответствующий всем общепризнанным мировым стандартам (в частности ISO 9000). Как видим, внедрение Rose на предприятии позволяет, в дополнение к вышеописанному, структурировать сопроводительную документацию, привести ее к необходимому стандарту с минимальными девиациями.
Разработчикам. Не меньше возможностей Rose дает и разработчикам. Давайте снова повторим очевидную вещь: информационные системы конца 90 гг. вышли на такой уровень сложности, что справиться с ними уже под силу только крупным компаниям с большим количеством узкоспециализированных разработчиков. Времена программистов-одиночек ушли в небытие. В современных условиях механизм “сам все сделаю” дает явный сбой. В качестве второй проблемы можно отметить некоторую текучесть кадров на отдельно взятом предприятии. Каждый раз, при включении нового сотрудника в проект, необходимо посвящать его во все детали проекта, на что уходит драгоценное время коллег, отрываемых от основной работы. При наличии же Rose достаточно показать все диаграммы проекта и предоставить проектную документацию, сгенерированную на основе полученной модели, как все станет на свои места. Разработчик увидит как весь проект в целом, так и свою часть. Конкретно же в плане разработки Rose поддерживает проектирование, основанное на двух способах: прямом и обратном. В режиме прямого проектирования разработчик рисует определенным образом диаграммы классов и их взаимодействия, а на выходе получает сгенерированный код на определенном языке программирования. В режиме же обратного проектирования возможно построение модели на базе имеющегося исходного кода. Из этого следует самая главная возможность для разработчиков: повторное проектирование (Round-trip), когда разработчик описывает классы в Rose, генерирует код, дописывает необходимую полнофункциональную часть и снова закачивает в Rose, для представления того, что же система получила в результате его действий.
Важнейшим свойством Rational Rose принято считать открытость архитектуры, что позволяет дополнять имеющийся в ней инструментарий новыми функциями. Например, в стандартном варианте Rose не поддерживает кодогенерацию на Ассемблере… Путем написания дополнительного модуля подобную проблему можно решить. Спешу обрадовать, на Западе достаточно компаний, выпускающих подобные модули расширения для различных языков программирования, правда, на Ассемблере еще нет, но мы будем надеяться!
Вот список включенных стандартных модулей: С++, ADA, CORBA, Visual Basic, XML, COM, Oracle). То есть Rational Rose способна проводить прямое и обратное проектирование в данных системах.
Итак, от общих тем перейдем непосредственно к тому, что умеет делать CASE Rational Rose. Являясь объектно-ориентированным инструментом моделирования, Rose базируется на UML (Universal Modeling Language) – универсальном языке моделирования, который был разработан компанией Rational именно с целью создания наиболее оптимального и универсального языка для описания как предметной области, так и конкретной задачи в программировании. Любая задача программируется при помощи определенных диаграмм. UML поддерживает построение следующих диаграмм:
Все разработчики сталкиваются с ситуацией, когда приходится проектировать большие классы. При ручном вводе и объявлении имеется ряд подводных камней: во-первых, постановщик задач, как правило, описывает “что нужно” на словах, в крайнем случае, с минимальным бумажным сопровождением; во-вторых, разработчик, создающий систему, опять-таки в большинстве случаев игнорирует все комментарии, которыми необходимо сопровождать программный код. Что же получается в итоге? Постановщик задач путается в программе, разработчик сам не помнит, что к чему, а если на его место взят новый сотрудник… Тут на ум приходит еще одно традиционное для России высказывание разработчика: “мне проще все написать заново”. И ведь пишут… Тормозя производство программного продукта. Дело в том, что к разработке ПО относятся как к искусству, а необходимо относиться, как к производственному процессу со строгим распределением ролей, полномочий и пр…
На нижеследующем примере я продемонстрирую:
Class String{ Private:
Int Counter;
Int Stat;
Public:
Char *TmpString;
Int GetStringSize(Char *);
Int PrintString(Char *);
IntCmpString(Char *, Char *);
Int CpyString(Char *, Char *);
};
Теперь средствами Rose все спроектируем в графическом виде.
Рис 1 |
Для отображения классов в Rose используется диаграмма классов. В качестве имени задаем “MyString”. Далее, войдя в саму диаграмму, необходимо расписать спецификации: атрибуты (типы переменных) и операции (функции класса с формальным описанием всех параметров на входе и на выходе).
Каждый атрибут задается отдельно, ему дается комментарий, и расписывается тип (public, protected, private). На рис. 1 показан разворот спецификации для TmpString. Подобным образом расписываются все переменные.
В плане описания функций все похоже, только помимо описания самой функции (тип возвращаемого значения) необходимо расписать специфику каждого входного параметра, снабдив все это подробнейшими комментариями. Для того чтобы разработчик не считал притеснением комментирование каждого шага, во всех продуктах компании Rational принято давать комментарии для любой малозаметной операции, поскольку впоследствии, при генерации отчетов не нужно будет еще раз руками доводить документ, чтобы показать его руководству или передать разработчику в качестве технического задания.
Рис 2 |
Результатом выполнения вышеописанных действий будет появление класса с расписанными спецификациями. Сам класс показан на рис. 2. Можно отметить, что в графическом виде можно оценить основные свойства каждого элемента.
Следующий шаг в работе - получение кода на С++. Здесь хочется развеять существующий миф о 100% генерации кода. Rational Rose 2000e Enterprise в принципе не может дать готового кода, она способна лишь спроектировать класс и расписать спецификацию каждого элемента, подставить шаблоны членов класса для дальнейшего заполнения кодом. А вот для 100% генерации рабочего кода на С++ используется Rational Rose 2000 RealTime, которую в данном случае мы не рассматриваем.
Итак, вернемся к кодогенерации (точнее сказать, к классо-генерации). Через систему меню (Tools) выбираем поддерживаемый язык для описания спроектированного класса (в данном случае это С++), вызываем “Code Generationаl”. Результатом работы будет появление двух файлов: MyString.h и MyString.cpp. В первом расписывается сам класс, а второй является шаблоном для дальнейшего заполнения соответствующим кодом.
Ниже приводятся распечатки обоих файлов, для того чтобы вы имели представление о том, какой код вы получите, применяя Rational Rose.
Весь приведенный материал получен без изменения настроек и без дополнительной правки.
Имея подобный шаблон, становится не важно, какой именно разработчик начал работать над кодированием логики класса.
Для получения же подробного отчета по классу или технического задания можно воспользоваться инструментом Rational SoDA. Отчет также приведен в конце статьи.
Следующая задача, с которой поможет справиться Rational Rose – анализ существующей системы. Зачем переписывать и документировать крупные системы заново, если можно воспользоваться функцией обратного проектирования, что позволит из имеющегося кода построить визуальную модель и уже визуально дописать необходимые свойства и атрибуты, дописать новые классы. А под конец сгенерировать весь спектр файлов, необходимых для дальнейшей работы программистов. Данный подход называется Round-Trip и полностью поддерживается в RationalRose.
Новая версия Rose - 2000e - поддерживает полноценное проектирование баз данных. Раньше использование Rose ограничивалось только высокоуровневым проектированием, а доводкой занимались в ERwin, для чего был создан специальный мост ERwin RoseLink сторонней компанией, что создавало определенные неудобства в работе. Теперь же Rose получила достаточно выразительные средства для лучшего проектирования баз данных, а мост с ERwin включен в стандартную поставку, что позволит более не опасаться за надежность передаваемых моделей.
Подобная интеграция не может не вызывать восхищения. Наконец-то можно полностью проектировать все в Rose! По словам Роджера Оберга (Roger Oberg), вице-президента Rational: "Rational Rose стала стандартом при разработке приложений и бизнес-моделировании. Теперь, вместе с Rational Rose, проектировщики БД могут воспользоваться преимуществами UML и присоединиться к остальным разработчикам".
Использование UML для моделирования и дизайна баз данных не только облегчает коммуникацию и убирает преграды между разработчиками, но и дает им более выгодную среду для моделирования. С моделью базы данных на языке UML проектировщик БД может получать такую информацию, как ограничения (constraints), триггеры и индексы прямо на диаграмме. Когда эта информация смоделирована, пользователям становится проще обеспечивать коммуникацию с моделью базы данных в целом.
В дополнение к преимуществам от использования UML для моделирования баз данных Rational Rose дает следующие возможности:
Файл MyString.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%395AF70D0321.cm preserve=no // %X% %Q% %Z% %W% //## end module%395AF70D0321.cm //## begin module%395AF70D0321.cp preserve=no //## end module%395AF70D0321.cp //## Module: MyString%395AF70D0321; Pseudo Package specification //## Source file: C:\Program Files\Rational\Rose\C++\source\MyString.h #ifndef MyString_h #define MyString_h 1 //## begin module%395AF70D0321.additionalIncludes preserve=no //## end module%395AF70D0321.additionalIncludes //## begin module%395AF70D0321.includes preserve=yes //## end module%395AF70D0321.includes //## begin module%395AF70D0321.additionalDeclarations preserve=yes //## end module%395AF70D0321.additionalDeclarations //## begin MyString%395AF70D0321.preface preserve=yes //## end MyString%395AF70D0321.preface //## Class: MyString%395AF70D0321 // Данный класс позволяет проводить различные операции над // массивами символов. //## Category: <Top Level> //## Persistence: Transient //## Cardinality/Multiplicity: n class MyString { //## begin MyString%395AF70D0321.initialDeclarations preserve=yes //## end MyString%395AF70D0321.initialDeclarations public: //## Constructors (generated) MyString(); //## Destructor (generated) ~MyString(); //## Assignment Operation (generated) MyString & operator=(const MyString &right); //## Equality Operations (generated) int operator==(const MyString &right) const; int operator!=(const MyString &right) const; //## Other Operations (specified) //## Operation: GetStringSize%395AF87900E9 // Подсчитывает количество символов в переданном массиве Int GetStringSize (Char *massiv // Указатель на массив ); //## Operation: PrintString%395AF88800B9 // Печатает на экране переданный массив Int PrintString (Char *Massiv // Указатель на массив ); //## Operation: CmpString%395AF892013F // Сравнивает два массива. Int CmpString (Char *Str1, // Указатель на первый массив Char *Str2 // Указатель на второй массив ); //## Operation: CpyString%395AF89C00D5 // Копирует один массив в другой Int CpyString (Char *Dest, // Назначение Char *Source // Источник ); //## Get and Set Operations for Class Attributes (generated) //## Attribute: Stat%395AF8BB0289 // Общедоступная переменная числа обращений к PrintString const Int get_Stat () const; void set_Stat (Int value); //## Attribute: Count%395AF8C20148 // Определяет статус определенного объекта const Int get_Count () const; void set_Count (Int value); // Additional Public Declarations //## begin MyString%395AF70D0321.public preserve=yes //## end MyString%395AF70D0321.public protected: // Additional Protected Declarations //## begin MyString%395AF70D0321.protected preserve=yes //## end MyString%395AF70D0321.protected private: //## Get and Set Operations for Class Attributes (generated) //## Attribute: TmpString%395AF8B201E5 // Временный указатель на строковый массив. Можно // использовать в качестве буфера const Char * get_TmpString () const; void set_TmpString (Char * value); // Additional Private Declarations //## begin MyString%395AF70D0321.private preserve=yes //## end MyString%395AF70D0321.private private: //## implementation // Data Members for Class Attributes //## begin MyString::TmpString%395AF8B201E5.attr preserve=no private: Char * {U} Char *TmpString; //## end MyString::TmpString%395AF8B201E5.attr //## begin MyString::Stat%395AF8BB0289.attr preserve=no public: Int {U} Int Stat; //## end MyString::Stat%395AF8BB0289.attr //## begin MyString::Count%395AF8C20148.attr preserve=no public: Int {U} Int Count; //## end MyString::Count%395AF8C20148.attr // Additional Implementation Declarations //## begin MyString%395AF70D0321.implementation preserve=yes //## end MyString%395AF70D0321.implementation }; //## begin MyString%395AF70D0321.postscript preserve=yes //## end MyString%395AF70D0321.postscript // Class MyString //## Get and Set Operations for Class Attributes (inline) inline const Char * MyString::get_TmpString () const { //## begin MyString::get_TmpString%395AF8B201E5.get preserve=no return TmpString; //## end MyString::get_TmpString%395AF8B201E5.get } inline void MyString::set_TmpString (Char * value) { //## begin MyString::set_TmpString%395AF8B201E5.set preserve=no TmpString = value; //## end MyString::set_TmpString%395AF8B201E5.set } inline const Int MyString::get_Stat () const { //## begin MyString::get_Stat%395AF8BB0289.get preserve=no return Stat; //## end MyString::get_Stat%395AF8BB0289.get } inline void MyString::set_Stat (Int value) { //## begin MyString::set_Stat%395AF8BB0289.set preserve=no Stat = value; //## end MyString::set_Stat%395AF8BB0289.set } inline const Int MyString::get_Count () const { //## begin MyString::get_Count%395AF8C20148.get preserve=no return Count; //## end MyString::get_Count%395AF8C20148.get } inline void MyString::set_Count (Int value) { //## begin MyString::set_Count%395AF8C20148.set preserve=no Count = value; //## end MyString::set_Count%395AF8C20148.set } //## begin module%395AF70D0321.epilog preserve=yes //## end module%395AF70D0321.epilog #endif
Приложение №2
Пример кодогенерации.
Файл MyString.cpp
//## 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%395AF70D0321.cm preserve=no
// %X% %Q% %Z% %W%
//## end module%395AF70D0321.cm
//## begin module%395AF70D0321.cp preserve=no
//## end module%395AF70D0321.cp
//## Module: MyString%395AF70D0321; Pseudo Package body
//## Source file: C:\Program Files\Rational\Rose\C++\source\MyString.cpp
//## begin module%395AF70D0321.additionalIncludes preserve=no
//## end module%395AF70D0321.additionalIncludes
//## begin module%395AF70D0321.includes preserve=yes
//## end module%395AF70D0321.includes
// MyString
#include "MyString.h"
//## begin module%395AF70D0321.additionalDeclarations preserve=yes
//## end module%395AF70D0321.additionalDeclarations
// Class MyString
MyString::MyString()
//## begin MyString::MyString%395AF70D0321_const.hasinit preserve=no
//## end MyString::MyString%395AF70D0321_const.hasinit
//## begin MyString::MyString%395AF70D0321_const.initialization preserve=yes
//## end MyString::MyString%395AF70D0321_const.initialization
{
//## begin MyString::MyString%395AF70D0321_const.body preserve=yes
//## end MyString::MyString%395AF70D0321_const.body
}
MyString::~MyString()
{
//## begin MyString::~MyString%395AF70D0321_dest.body preserve=yes
//## end MyString::~MyString%395AF70D0321_dest.body
}
MyString & MyString::operator=(const MyString &right)
{
//## begin MyString::operator=%395AF70D0321_assign.body preserve=yes
//## end MyString::operator=%395AF70D0321_assign.body
}
int MyString::operator==(const MyString &right) const
{
//## begin MyString::operator==%395AF70D0321_eq.body preserve=yes
//## end MyString::operator==%395AF70D0321_eq.body
}
int MyString::operator!=(const MyString &right) const
{
//## begin MyString::operator!=%395AF70D0321_neq.body preserve=yes
//## end MyString::operator!=%395AF70D0321_neq.body
}
//## Other Operations (implementation)
Int MyString::GetStringSize (Char *massiv)
{
//## begin MyString::GetStringSize%395AF87900E9.body preserve=yes
//## end MyString::GetStringSize%395AF87900E9.body
}
Int MyString::PrintString (Char *Massiv)
{
//## begin MyString::PrintString%395AF88800B9.body preserve=yes
//## end MyString::PrintString%395AF88800B9.body
}
Int MyString::CmpString (Char *Str1, Char *Str2)
{
//## begin MyString::CmpString%395AF892013F.body preserve=yes
//## end MyString::CmpString%395AF892013F.body
}
Int MyString::CpyString (Char *Dest, Char *Source)
{
//## begin MyString::CpyString%395AF89C00D5.body preserve=yes
//## end MyString::CpyString%395AF89C00D5.body
}
// Additional Declarations
//## begin MyString%395AF70D0321.declarations preserve=yes
//## end MyString%395AF70D0321.declarations
//## begin module%395AF70D0321.epilog preserve=yes
//## end module%395AF70D0321.epilog
Приложение №3
Пример отчета в SoDA
Class Report
MyString
Issue <1.0>
Revision History
Date | Issue | Description | Author |
<dd/mmm/yy> | <x.x> | <details> | <name> |
Table of Contents
Данный класс позволяет проводить различные операции над массивами символов.
No operations of stereotype <<responsibility>> have been defined.
CmpString (Str1: Char *, Str2: Char *): Int
Documentation: Сравнивает два массива.
CpyString (Dest: Char *, Source: Char *): Int
Documentation: Копирует один массив в другой
GetStringSize (massiv: Char *) : Int
Documentation: Подсчитывает количество символов в переданном массиве
PrintString (Massiv: Char *): Int
Documentation: Печатает на экране переданный массив
TmpString: Char *
Documentation: Временный указатель на строковый массив. Можно использовать в качестве буфера
Stat: Int
Documentation: Общедоступная переменная числа обращений к PrintString
Count: Int
Documentation: Определяет статус определенного объекта
There is no state diagram defined for class MyString.
Следующая часть
Дополнительная информация
За дополнительной информацией обращайтесь в компанию Interface Ltd.
INTERFACE Ltd. |
|