Самоучитель по UML

         

Если попытаться охарактеризовать современный уровень



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

Методология объектно-ориентированного


Приведенное выше определение класса является достаточно общим. В последующих главах по мере изучения материала этот термин будет уточняться на основе установления семантических связей с другими понятиями объектно-ориентированного анализа и проектирования.

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

Иерархия понятий строится следующим образом. В качестве наиболее общего понятия или категории берется понятие, имеющее наибольший объем и, соответственно, наименьшее содержание. Это самый высокий уровень абстракции для данной иерархии. Затем данное общее понятие некоторым образом конкретизируется, тем самым уменьшается его объем и увеличивается содержание. Появляется менее общее понятие, которое на схеме иерархии будет расположено на уровень ниже исходного понятия. Этот процесс конкретизации понятий может быть продолжен до тех пор, пока на самом нижнем уровне не будет получено понятие, дальнейшая конкретизация которого в данном контексте либо невозможна, либо нецелесообразна.

Примерами наиболее общих понятий могут служить такие абстрактные категории, как система, структура, интеллект, информация, сущность, связь, состояние, событие и многие другие. В процессе изучения этих категорий появляются новые особенности их содержания и объема. Именно по этим причинам всегда трудно дать им точное определение.
В качестве примеров конкретных понятий можно привести понятие книги, которую читатель держит в руках, или понятие микропроцессора Intel Pentium П-300.

Примечание

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

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

Для иллюстрации принципа наследования можно привести следующий пример. Рассмотрим в качестве общего класс "Автомобиль". Данный класс определяется как некоторая абстракция свойств и поведения всех реально существующих автомобилей. При этом свойствами класса "Автомобиль" могут быть такие общие свойства, как наличие двигателя, трансмиссии, колес, рулевого управления. Если в качестве производного класса рассмотреть класс "Легковой автомобиль", то все выделенные выше свойства будут присущи и этому классу. Можно сказать, что класс "Легковой автомобиль" наследует свойства родительского класса "Автомобиль". Однако, кроме перечисленных свойств, класс-потомок будет содержать дополнительные свойства, например такое, как наличие салона с количеством посадочных мест 2—5.



В свою очередь, класс " Легковой автомобиль" способен порождать другие подклассы, которые вполне могут соответствовать, например, моделям конкретных фирм-производителей. Таким образом, можно рассматривать класс "Легковой автомобиль производства ВАЗ". Поскольку Волжский автомобильный завод выпускает несколько моделей автомобилей, одним из производных классов для предыдущего класса может быть конкретная модель автомобиля, например, ВАЗ-21083. Наконец, изготовленный автомобиль имеет уникальный заводской номер, отличающий один автомобиль от другого. Таким номером может быть, например, XTA-210830S1594301. В последнем случае класс будет состоять из единственного объекта или экземпляра, которым будет являться легковой автомобиль производства ВАЗ с указанным выше заводским номером.

Описанная выше информация о соотношении классов в нашем примере обладает одним серьезным недостатком, а именно отсутствием наглядности. В этой связи возникает вопрос: а возможно ли представить иерархию наследования классов в визуальной форме? Традиционно для изображения понятий в формальной логике использовались окружности или прямоугольники. Тогда для рассмотренного примера иерархия порождения классов может быть представлена в виде вложенных прямоугольников, каждый из которых соответствует отдельному классу (рис. 1.2).







Рис. 1.2. Иерархия вложенности классов для примера "Автомобиль"

Появление объектно-ориентированных языков программирования было связано с необходимостью реализации концепции классов и объектов на синтаксическом уровне. С точки зрения ООП класс является дальнейшим расширением структуры (structure) или записи (record). Включение в известные языки программирования С и Pascal классов и некоторых других возможностей привело к появлению соответственно C++ и Object Pascal, которые на сегодня являются наиболее распространенными языками разработки приложений. Распространению C++ и Object Pascal способствовало то обстоятельство, что язык C++ был выбран в качестве базового для программного инструментария MS Visual C++, а язык Object Pascal— для популярного средства быстрой разработки приложений Borland/Inprise Delphi.



За короткий период времени оба инструментария превратились в мощные системы разработки программ с соответствующими библиотеками стандартных классов, содержащих сотни различных свойств и методов. Применительно к среде MS Visual C++ 5/6 такая библиотека имеет специальное название — MFC (Microsoft Foundation Classes), т. е. фундаментальные классы от Microsoft. При этом производные классы наследуют свойства и методы родительских классов. Ниже приводится фрагмент иерархии классов MFC в том виде, как он изображен в соответствующей документации (рис. 1.3).





Рис. 1.3. Фрагмент иерархии классов MFC, используемых в среде программирования MS Visual C++ 5/6



Рис. 1.4. Фрагмент иерархии классов VCU используемых в среде программирования Borland/Inprise Delphi 3-4

Процесс разработки программ в среде Borland/Inprise Delphi также тесно связан с использованием библиотеки стандартных классов — VCL (Visual Component Library) или библиотеки визуальных компонентов. Эта библиотека тоже построена по иерархическому принципу, в соответствии с которым компоненты нижележащих уровней наследуют свойства и методы вышележащих компонентов. Для данного случая также приводится фрагмент иерархии классов VCL (рис. 1.4).

Даже этих простых примеров достаточно, чтобы понять следующий факт. А именно, для одной и той же общей концепции иерархии классов используются совершенно различные графические средства. В первом случае — вложенные прямоугольники, во втором — связные прямоугольники. В действительности различных способов изображения классов предложено гораздо больше, небольшая часть из них будет рассмотрена ниже. Однако уже сейчас важно осознать, что подобную ситуацию следовало бы унифицировать, т. е. использовать для этой цели некоторую единую систему обозначений.

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


Конкретная реализация присущих классу свойств и методов, которые определяют поведение этого класса, является собственным делом данного класса. Более того, отдельные свойства и методы класса вообще могут быть невидимы за пределами этого класса, что является базовой идеей введения различных категорий видимости для компонентов класса.

Если продолжить рассмотрение примера с классом "Легковой автомобиль", то нетрудно проиллюстрировать инкапсуляцию следующим образом. Основным субъектом, который взаимодействует с этим классом, является водитель. Вполне очевидно, что не каждый водитель в совершенстве знает внутреннее устройство легкового автомобиля. Более того, отдельные детали этого устройства сознательно скрыты в корпусе двигателя или в коробке передач. А в случае нарушения работы автомобиля, являющейся причиной неадекватности его поведения, необходимый ремонт выполняет профессиональный механик.

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

Принцип разделения модуля на интерфейс и реализацию отражает суть наших представлений об окружающем мире. В интерфейсной части указывается вся информация, необходимая для взаимодействия с любыми другими объектами. Реализация скрывает или маскирует от других объектов все детали, не имеющие отношения к процессу взаимодействия объектов (рис. 1.5).



Рис. 1.5. Иллюстрация сокрытия внутренних деталей реализации методов классов

Примечание

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


Если ассоциировать реализацию программного модуля с. водой в аквариуме, то видимость объектов, находящихся в воде, будет зависеть от степени ее чистоты или загрязнения. В ООП существуют различные варианты доступа к свойствам и методам классов, которые получили название видимости свойств и методов. В этом случае использование различных форм видимости для компонентов классов удобно ассоциировать с прозрачностью фона рисунка или видимостью в воде аквариума. Более детальное рассмотрение различных форм видимости приводится в части II книги.

Третьим принципом ООП является полиморфизм. Под полиморфизмом (греч. Poly— много, morfos — форма) понимают свойство некоторых объектов принимать различные внешние формы в зависимости от обстоятельств. Применительно к ООП полиморфизм означает, что действия, выполняемые одноименными методами, могут отличаться в зависимости от того, какому из классов относится тот или иной метод.

Рассмотрим, например, три объекта или класса: двигатель автомобиля, электрический свет в комнате и персональный компьютер. Для каждого из них можно определить операцию "выключить". Однако сущность этой операции будет отличаться для каждого из рассмотренных объектов. Так для двигателя автомобиля вызов метода двигатеяь_автомобиля. выключить о означает прекращение подачи топлива и его остановку. Вызов метода Комната. электрический_ свет. выключить о означает простой щелчок выключателя, после чего комната погрузится в темноту. В последнем случае действие персональный_ компьютер. выключить о может быть причиной потери данных, если выполняется нерегламентированным образом.

Примечание

В рассмотренном выше примере использовалась одна из принятых нотаций в некоторых языках программирования (например, в Object Pascal) для обозначения принадлежности метода тому или иному классу. В соответствии с этой нотацией, вначале указывается имя класса, в котором определен метод, а затем через точку имя самого метода. Если метод определен в некотором подклассе, то должна быть указана вся цепочка классов, начиная с наиболее общего из них.


При этом характерным признаком метода является пара скобок, которые используются для указания списка аргументов или формальных параметров данного метода.

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

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

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


Реакция программы при этом тоже связывается с последующими событиями.

Наиболее существенным обстоятельством в развитии методологии ООП явилось осознание того факта, что процесс написания программного кода может быть отделен от процесса проектирования структуры программы. Действительно, до того как начать программирование классов, их свойств и методов, необходимо определить, чем же являются сами эти классы. Более того, нужно дать ответы на такие вопросы, как: сколько и какие классы нужно определить для решения поставленной задачи, какие свойства и методы необходимы для придания классам требуемого поведения, а также установить взаимосвязи между классами.

Эта совокупность задач не столько связана с написанием кода, сколько с общим анализом требований к будущей программе, а также с анализом конкретной предметной области, для которой разрабатывается программа. Все эти обстоятельства привели к появлению специальной методологии, получившей название методологии объектно-ориентированнного анализа и проектирования (ООАП).


Методология процедурно-ориентированного


Принято считать, что сам термин алгоритм происходит от имени средневекового математика Аль-Хорезми, который в 825 г. описал правила выполнения арифметических действий в десятичной системе счисления.

С этой точки зрения вся история математики тесно связана с разработкой тех или иных алгоритмов решения актуальных для своей эпохи задач. Более того, само понятие алгоритма стало предметом соответствующей теории — теории алгоритмов, которая занимается изучением общих свойств алгоритмов. Со временем содержание этой теории стало настолько абстрактным, что соответствующие результаты понимали только специалисты. Как дань этой традиции какой-то период времени языки программирования назывались алгоритмическими, а первое графическое средство документирования программ получило название блок-схемы алгоритма. Соответствующая система графических обозначений была зафиксирована в ГОСТ 19.701-90, который регламентировал использование условных обозначений в схемах алгоритмов, программ, данных и систем.

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

Со временем разработка больших программ превратилась в серьезную проблему и потребовала их разбиения на более мелкие фрагменты. Основой для такого разбиения как раз и стала процедурная декомпозиция, при которой отдельные части программы или модули представляли собой совокупность процедур для решения некоторой совокупности задач. Главная особенность процедурного программирования заключается в том, что программа' всегда имеет начало во времени или начальную процедуру (начальный блок) и окончание (конечный блок).
При этом вся программа может быть представлена визуально в виде направленной последовательности графических примитивов или блоков (рис. 1.1).

Важным свойством таких программ является необходимость завершения всех действий предшествующей процедуры для начала действий последующей процедуры. Изменение порядка выполнения этих действий даже в пределах одной процедуры потребовало включения в языки программирования специальных условных операторов типа if-then-eise и Goto для реализации ветвления вычислительного процесса в зависимости от промежуточных результатов решения задачи.





Рис. 1.1. Графическое представление программы в виде последовательности процедур

Примечание

Появление и интенсивное использование условных операторов и оператора безусловного перехода стало предметом острых дискуссий среди специалистов по программированию. Дело в том, что бесконтрольное применение в программе оператора безусловного перехода goto способно серьезно осложнить понимание кода. Соответствующие программы стали сравнивать со спагетти, называя их bowl of spaghetti, имея в виду многочисленные переходы от одного фрагмента программы к другому, или, что еще хуже, возврат от конечных операторов программы к ее начальным операторам. Ситуация казалась настолько драматичной, что в литературе зазвучали призывы исключить оператор goto из языков программирования. Именно с этого времени принято считать хорошим стилем программирования — программирование без goto.

Рассмотренные идеи способствовали становлению некоторой системы взглядов на процесс разработки программ и написания программных кодов, которая Получила название методологии структурного программирования. Основой данной методологии является процедурная декомпозиция программной системы и организация отдельных модулей в виде совокупности выполняемых процедур. В рамках данной методологии получило развитие нисходящее проектирование программ или программирование "сверху-вниз". Период наибольшей популярности идей структурного программирования приходится на конец 70-х—начало 80-х годов.



Как вспомогательное средство структуризации программного кода было рекомендовано использование отступов в начале каждой строки, которые должны выделять вложенные циклы и условные операторы. Все это призвано способствовать пониманию или читабельности самой программы. Данное правило со временем было реализовано в современных инструментариях разработки программ. Ниже приводится пример листинга программы на языке Pascal, который иллюстрирует эту особенность написания программ.

Листинг 1.1. Пример фрагмента программы на Pascal, разработанной с использованием правил структурного программирования

Procedure FirstOpt; 

  Begin

    FuncRaz(Free, Rn); 

    for i:=l to N do

      RvarRec[i]:= Rn[i]; 

    FvarRec:= Freс; 

    Numlt:=0; 

    Repeat

      NumIt:=NumIt+l; 

      V:= Freс; 

        for j:=1 to К do 

          for 1:=1 to M do 

            begin

              S:=0.0; 

              T:=0.0;

              for i:=l to N do 

                begin

                  T:=T+sqr(Wl[i,j])*Xpr[i,l];

                  S:=S+sqr(Wl[i,j])

                end;

              Zentr[j,l]:=T/S 



            end;

          for j:= 1 to К do 

            for i:=l to N do 

              begin

                S:=0.0; 

                P:=0.0; 

                Q:=0.0; 

                for l:=1 to M do

                  S:=S+sqr(Xpr[i,l]-Zentr[j,l]); 

                P:=1.0/S; 

              end; 

            Q:=0.0; 

            D:=0;

            for i:=1 to N do 

              for j:=1 to К do

                if Abs(Wl[i,j]-W2[i,j]) >= Eps then D:=l; 

              for i:=l to N do

                for j:=1 to К do

                  W1[i,j]:=W2[i,j] 

  Until (D=0)or(NumIt=NumMax) 



End;

В этот период основным показателем сложности разработки программ считали ее размер. Вполне серьезно обсуждались такие оценки сложности программ, как количество строк программного кода. Правда, при этом делались некоторые предположения относительно синтаксиса самих строк, которые должны были удовлетворять определенным правилам. Общая трудоемкость разработки программ оценивалась специальной единицей измерения — "человеко-месяц" или "человеко-год". А профессионализм программиста напрямую связывался с количеством строк программного кода, который он мог написать и отладить в течение, скажем, месяца.

Примечание

Сейчас попытки оценить профессионализм программиста количеством строк программного кода могут вызвать лишь улыбку собеседника. Действительно, используя встроенные мастера современных инструментариев разработки (MS Visual C++ или Inprise/Borland Delphi), даже новичок может за считанные секунды последовательным нажатием кнопок диалоговых меню создать работоспособное приложение, содержащее сотни строк программного кода и состоящее из десятка отдельных файлов проекта.



Методология системного анализа


Термин "моделирование" имеет довольно много смысловых оттенков, например, моделирование одежды или моделирование прически. Не отрицая важности этих сфер творчества, следует отметить, "что все эти аспекты моделирования лежат за пределами книги. Рассмотрение особенностей языка UML связано с вопросами логического или информационного моделирования систем.

Наиболее общей моделью системы является так называемая модель "черного ящика". В этом случае система представляется в виде прямоугольника, внутреннее устройство которого скрыто от аналитика или неизвестно. Однако система не является полностью изолированной от внешней среды, поскольку последняя оказывает на систему некоторые информационные или материальные воздействия. Такие воздействия получили название входных воздействий. В свою очередь, система также оказывает на среду или другие системы определенные информационные или материальные воздействия, которые получили название выходных воздействий. Графически данная модель может быть изображена следующим образом (рис. 1.7).

Рис. 1.7. Графическое изображение модели системы в виде "черного ящика"

Ценность моделей, подобных модели "черного ящика", весьма условна. Невольно может возникнуть ассоциация с "Черным квадратом". Однако если оценка изобразительных особенностей последнего не входит в задачи системного анализа, то общая модель системы содержит некоторую важную инфомацию о функциональных особенностях данной системы, которые дают представление о ее поведении. Действительно, кроме самой общей информации о том, на какие воздействия реагирует система, и как проявляется эта реакция на окружающие объекты и системы, другой информации мы получить не можем. В рамках системного анализа разработаны определенные методологические средства, позволяющие выполнить дальнейшую конкретизацию общей модели системы. Некоторые из графических средств представления моделей систем будут рассмотрены в главе 2.

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

Сложность системы и, соответственно, ее модели может быть рассмотрена с различных точек зрения. Прежде всего, можно выделить сложность структуры системы, которая характеризуется количеством элементов системы и различными типами взаимосвязей между этими элементами. Если количество элементов превышает некоторое пороговое значение, которое не является строго фиксированным, то такая система может быть названа сложной. Например, если программная СУБД насчитывает более 100 отдельных форм ввода и вывода информации, то многие программисты сочтут ее сложной. Транспортная система современных мегаполисов также может служить примером сложной системы.

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