Обзоры
ООП и UML: коротко и без мифов
207

ООП и UML: коротко и без мифов

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

Сирил Н. Паркинсон


ООП как взгляд на мир

Хотя Платон и истина мне дороги, однако священный долг велит отдать предпочтение
истине.

Аристотель

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

Рис. 1. Редактор UML-диаграмм
Dia
Рис. 2. CASE-средство и редактор
UML-диаграмм Poseidon
Рис. 3. Элементы Use Case-модели
в UML-редакторе TCM
Рис. 4. UML-моделлер Thorn
для платформы Windows
Рис. 5. "Букварь"
UML

Третий философский период бесед Платона с учениками дал
миру учение об "идеях" (именно так оно чаще всего называется в русскоязычной
философской литературе), или "forms" (этот, на взгляд автора, семантически
более подходящий термин обычно используется в англоязычной литературе). Forms
Платона — создаваемые человеческим интеллектом неизменные абстрактные модели
"предметов вообще" из меняющегося реального мира. Так, form "лошадь"
обозначает "лошадь вообще" — безотносительно породы, масти и т. д.
— и позволяет распознать в конкретном животном именно лошадь.

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

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

Вооружившись краткими историческими сведениями, приступим непосредственно к
"главному виновнику" — объектно-ориентированному проектированию.
Раз мы ведем речь о деятельности человека, в полном соответствии с учением Платона
нас будет интересовать именно то, что он назвал forms. Изменяющийся реальный
мир в данном случае предельно прост: он состоит из предметов (сущностей), отношений
между предметами и событий, происходящих в мире предметов. По сути, ООП представляет
собой некий свод принципов, на основе которых, созерцая реальный мир, можно
строить forms, его моделирующие. Основные принципы ООП имеют "страшные"
названия, но на самом деле весьма просты и от процедур "группирования и
разделения" Платона практически ничем не отличаются. "Абстракции"
в терминах ООП — это и есть forms. "Инкапсуляция" — неотъемлемое
свойство процесса абстрагирования, заключающееся в игнорировании (сокрытии)
деталей и концентрации внимания на самом главном. "Наследование" —
аналог процедуры "разделения" Платона — построение новых forms с
уточненными свойствами на основе общей form. И наконец, — "полиморфизм"
(заметьте отголосок теории Платона в корне) — достаточно явное свойство возможных
отличий, порожденных процедурой "наследования" forms друг от друга.
По большому счету, на понятийном уровне четыре столпа ООП ничего принципиально
нового в теорию Платона не привнесли.

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

Вот, собственно, и все, что можно отыскать в теории ООП. "Как все? — спросит
удивленный читатель, — а как же знаменитое утверждение о том, что ООП — это
"серебряная пуля" (silver bullet) современной программной индустрии?!".
К сожалению, на этот вопрос у автора ответов нет — не зря же существует замечательная
шутка о сходстве ООП и… подросткового секса: никто этим не занимался, но все
этим хвастаются, потому что каждый считает, что кто-то другой все же это делал.

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

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

Отсутсвие формализации преподносит неожиданные сюрпризы, причем на самых начальных
этапах проектирования. К какой категории forms нужно, например, отнести "покупку"
(как процесс или свершившийся факт) — к классу, объекту (какого класса?), ассоциации
(между кем — покупателем и продавцом, покупателем и товаром, временем и товаром
и т. д.?), взаимодействию (опять же, между кем?). Чтобы ответить на подобные
вопросы, нужно сформировать forms для конкретной ситуации, в которой прочие
forms будут зависеть от form "покупка", а последняя — от прочих forms…
Проблема станет еще более серьезной при учете очевидного факта невозможности
"подъема" масштабного проекта силами одного специалиста — здесь появляется
коллектив, в котором каждый из проектировщиков имеет собственный багаж знаний
в моделируемой предметной области (реального мира) и собственную точку зрения
по этому поводу. Последнее — настоящий бич ООП, ведь все, что говорилось выше,
однозначно основывалось на предположении единственности лица, строящего моделирующие
forms.

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

Предположим, что:

  • в своих построениях мы используем form (назовем ее S),
    определяющую множество некоторых объектов (обозначим их Os). В программистской
    ООП-терминологии это же звучит так: мы используем объекты Os класса S (или
    типа S);
  • также мы используем form (назовем ее T), определяющую
    еще одно множество, но уже других объектов, которые мы будем именовать Ot
    (опять же в ООП-терминологии: объекты Ot класса T или типа T);
  • на основании form S и form T мы строим некоторую модель
    M (если хотите — программу M, использующую объекты Os и Ot классов/типов
    S и T соответственно).

Если в модели (программе) M замена всех объектов Ot на объекты
Os не приводит к изменению модели, то form S должна быть определена на основе
form T. В соответствии с уже рассмотренными терминами последнее означает, что
form S должна определяться с помощью процедуры "наследования" от form
T (на программистском языке — S должна быть подтипом T).

Как видите, даже весьма серьезный (можно сказать, основополагающий) принцип
ООП совсем не страшен. Но "нестрашность" не означает, что это панацея
(или пресловутая silver bullet), — доступность на понятийном уровне скрывает
очень опасно отточенный обоюдоострый кинжал. "Порезаться" им легко
— ведь проверку на Liskov-принцип надо проводить для всех (!) используемых
объектов, что в большом проекте может оказаться просто непосильной задачей.
Но все же Барбаре Лисков, которая почти десять лет назад сформулировала замечательное
правило, сказать "спасибо" стоит — без Liskov-принципа потерять равновесие
в ООП-мире слишком легко.

Так хорошо или плохо?

Действительно, в этой части нашего рассуждения осталось ответить на вопрос —
так хорош или плох на самом деле ООП-подход? И ответ здесь только один — ни
то, ни другое, если мы говорим об "ООП вообще". Для каждого конкретного
случая ООП (как и любой подход к проектированию) обладает и достоинствами, и
специфическими недостатками. Многие из недостатков можно выявить только в процессе
проектирования. И все же… Эти общие фразы несоизмеримо ближе к реальности,
чем бравурные утверждения об исключительных возможностях, радикальных ускорении
и снижении затрат. Просто сегодня это модно. А вчера было модно "структурное
проектирование", завтра будет модным "аспектный подход", послезавтра…
Так что оставим истеричные восторги в стороне и перейдем к обсуждению второго
предмета нашего экскурса в мир объектов.

UML

Унифицированный язык моделирования… Звучит, согласитесь, неплохо и даже многообещающе.
Поэтому сразу обратимся к охлаждающему восторг своим объемом главному документу
UML — "OMG Unified Modeling Language Specification". Согласно документу,
к созданию которого в той или иной степени причастны все эти гранды индустрии,
"UML — это язык для спецификации, визуализации, конструирования и документирования
артефактов программных систем, а также бизнес-процессов и других непрограммных
систем". Сразу сконцентрируем внимание на перечне "обязанностей"
UML. Спецификация, визуализация, конструирование и документирование — все это
имеет непосредственное отношение к высокоуровневому проектированию. А у высокоуровневого
"инструмента" не может быть одного-единственного аспекта применения,
поэтому осмелимся дополнить определение UML такой многоаспектной интерпретацией:

  • UML как метод используется для изучения поведения систем;
  • UML как язык используется для "вычленения"
    знаний о предметной области;
  • UML как моделирующий язык используется для понимания
    (и, возможно, формализации) закономерностей функционирования систем;
  • UML как унифицированный язык используется для координации
    деятельности разработчиков.

И наконец, сразу исключим возможные неправильные толкования:
UML не является ни визуальным языком программирования (что означает — на нем
нельзя писать исполняемых программ), ни инструментом (а, скорее, спецификацией
набора правил), ни процессом проектирования/конструирования (но, возможно, взглядом
на эти процессы).

Теперь можно не пугаться термина "язык", обычно применяемого к UML.
Забивать голову всякими ключевыми словами и именами процедур здесь не придется.
Зато картинки (иногда даже весьма потешные, в стиле наскальной живописи) —
это именно то, что представляет собой UML на визуальном уровне. Знакомство с
этими "веселыми картинками" мы начнем с самого ответственного этапа
UML-моделирования, предназначенного для… да для чего угодно. И это странное
высказывание сейчас будет понятно.

Диаграммы Use Case, вероятнее всего, мог рисовать тростинкой на песке во время
бесед с учениками еще сам Платон, ведь на этом этапе UML-моделирования приходится
работать с абстракциями (form) очень высокого, можно сказать, философского уровня.
Хотя бы потому, что их всего две: инициаторы и реакции (эти переводы умышленно
не дословны, потому что буквально и точно перевести идиому "actors and
cases" очень трудно). Form "инициатор" (актер, участник, пользователь…)
моделирует кого и что угодно — человека, компьютер, микросхему, программу.
Единственный критерий, по которому можно "отнести" нечто к представителям
form (класса) "инициатор", — оно (нечто) должно быть "внешним"
по отношению к моделируемой/проектируемой системе. Единственная "способность"
этого нечто заключается в инициировании поведенческих реакций (действий) системы.
Последние и называются представителями form/класса "реакция", в терминах
UML — cases.

Словарь UML для описания всего этого "разнообразия" крайне лаконичен
(рис. 3): схематичный человечек, обозначающий некоторого представителя form/класса
"инициатор"; эллипс, обозначающий представителя form/класса "реакция"
и отображающий некоторое множество действий; прямоугольник, "ограничивающий"
моделируемую систему (очевидно, что человечки должны быть нарисованы вне его);
и линии, отображающие процессы инициирования и "взаимоотношений" между
cases. Как раз из-за них и ведутся "битвы тяжеловесов ООП", о которых
мы вкратце упомянем. Нам же пора подвести краткий итог этому обзору Use Case-моделирования,
а именно ответить на главный вопрос — что же мы получаем в результате? Самое
точное и неразмытое определение звучит так: "неформальную спецификацию
действий и вариантов действий, которые система способна выполнять во взаимодействии
с ее пользователями". Неформальна она хотя бы потому, что для описания
всех элементов Use Case-диаграмм используются фразы или фрагменты текста, написанные
натуральным (человеческим) языком.

Первая разновидность упомянутых линий содержит единственную графическую форму
представления — просто отрезок — и отображает инициацию внутренней реакции
системы со стороны внешнего инициатора. Вторая же более обширна, но полностью
соответствует теории forms. Отрезок со стрелкой, соединяющий два эллипса, отображает
процедуру "наследования" из теории forms Платона, отрезок со стрелкой
и текстовым спецификатором "include" указывает на то, что одна form
содержит в себе другую (но не "наследует" ее), а такой же отрезок,
но со спецификатором "extend" указывает, что, возм ожно (но не обязательно),
перечень действий, моделируемый некоторой form, будет расширен действиями, определенными
другой form case. Именно вокруг extend-отношения между двумя cases ведутся настоящие
баталии — на практике очень трудно найти отличия, позволяющие использовать
эту, мягко говоря, странную конструкцию.

Вот, собственно, и все, что можно рассказать о Use Case-диаграммах, по крайней
мере, на принятом нами обзорном уровне. Кроме того, что они относятся к самым
пригодным к пониманию (трудно перевести английское слово understandability)
UML-моделям и широко используются не только при разработке ПО.

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

В этой части UML предлагает также не блещущий разнообразием набор графических
представлений forms (абстракций) всего двух уровней. Самый верхний — абстракция
"пакета" (package), группирующая множество абстракций со схожими свойствами/функциональностью.
"Пакеты" могут быть вложенными, при этом самый "большой"
из них, содержащий остальные, обычно именуется доменом (графическое представление
"пакета" приведено на рис. 5, а). Абстракция "пакета" интересна
забавным фактом — несмотря на то что в современных языках программирования
существуют механизмы, позволяющие ее реализовать (например, пространства имен
в C++), в большинстве источников о "пакетах" дословно говорится следующее:
обычно они используются из-за того, что распечатка UML-модели слишком велика
и не помещается на стандартный лист бумаги. "Классы" нам уже знакомы,
и в UML для их представления есть соответствующая картинка (рис. 5, б). Единственное,
что в ней может показаться новым, — это наличие двух полей — "Атрибуты"
и "Операции". Раньше мы уже говорили о философском смысле "инкапсуляции",
теперь наступила пора приподнять завесу и над реалиями. Они, естественно, отличаются
только нюансами. Form Платона (в компьютерном исполнении — "класс")
фактически остается прежней. Только теперь класс описывает множество объектов,
обладающих некоторым перечнем атрибутов и определенной функциональностью. Вот,
собственно, и все сложности. А инкапсуляция в данном случае означает, что реализация
этой функциональности "упрятана" в класс (как и некоторые атрибуты,
которые совершенно не важны для внешнего по отношению к классу наблюдателя).

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

Последний фрагмент нашего "букваря" — представление динамических
моделей. Они предназначены для моделирования общения в процессе исполнения программы
между объектами классов, разработанных с помощью статической модели. Это, можно
сказать, финальная часть UML-моделирования. Здесь словарь даже не элементарен,
он просто скуден. Фактически в нем два "слова" — графическое представление
объекта и факта "общения" между объектами (обычно в ООП именуемого
посылкой сообщения, рис. 5, в). Для факта посылки сообщения предусмотрен небольшой
перечень спецификаторов.

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

Что делать?

Действительно, после обсуждения Use Case-диаграмм (явно напоминающих попытку
формализации поиска ответа на риторический вопрос) оставить без внимания возможный
перечень доступных программ, помогающих приобщиться к миру "большого ООП",
было бы непростительно. Ведь высококлассные системы UML-моделирования настолько
дороги и функциональны, что речи об их использовании на начальном этапе обучения
(а тем более — самообучения) идти не может. Но далеко не все так плохо — пользователи
практически всех ОС, для которых есть работающая реализация Java, могут попробовать
хороший пакет Poseidon,
для предпочитающих ОС Windows есть вполне работоспособный UML-моделлер
Thorn
, для выбравших ОС Linux окажется подходящей достаточно функциональная
программа Dia
, а сторонники классических Unix-приложений могут воспользоваться
UML-моделлером
TCM
.

Ну и, конечно, — читать, читать, читать. Начать можно отсюда: www.cetuslinks.org/oo_uml.html,
а уж где придется закончить процесс самообучения, сказать трудно. Потому что
спрос на специалистов по высокоуровневому проектированию программ растет с каждым
днем.


Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: