Объектно-ориентированный подход и диаграммы классов в UML
Объектно-ориентированный подход. Основные принципы. Стадии разработки ПО. Пакеты. Общее понятие класса в UML. Атрибут. Операция. Импорт пакета. Интерфейс. Типы и классы реализации. Бинарная ассоциация. Множественность. Класс ассоциации. Агрегирование.
Рубрика | Программирование, компьютеры и кибернетика |
Вид | реферат |
Язык | русский |
Дата добавления | 03.10.2008 |
Размер файла | 366,2 K |
Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже
Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.
Объектно-ориентированный подход и диаграммы классов в UML
К.Ю.Романовский, С.В.Кузнецов, Д.В.Кознов
Аннотация
UML (Unified Modeling Language) с 1997 года является стандартом OMG в области визуального объектно-ориентированного моделирования и широко используется на практике, будучи реализован в рамках многих CASE-средств.
Данная работа содержит достаточно полное и доступное описание модели классов, лежащей в основе UML. Отсутствие подобных обзоров на русском языке делает эту работу особенно актуальной.
Введение
Психологи уже давно показали, что средний человек может одновременно воспринимать адекватно в пределах десятка единиц информации. Таким образом, при разработке программного обеспечения требуется минимизировать количество видимых программисту компонент. Человечество придумало несколько довольно эффективных подходов декомпозиции, т.е. разбиения большой задачи на меньшие подзадачи. Уже довольно давно применяется модульный подход, в котором большие программы разделяются на самостоятельные модули. Давно программы пишутся "сверху вниз" и с использованием структурной декомпозиции. Методы, основанные на этих подходах, имеют общую черту: в них данные и код, их обрабатывающий, существуют отдельно друг от друга.
В результате многолетних исследований был разработан и опробован так называемый объектно-ориентированный подход (первая ласточка в 1967 году - язык Simula67, а первый "сильный" выход в свет в 1983 году - язык C++). Одно из основных преимуществ ООП по сравнению с более ранними методами построения программных систем - тесная связь данных и кода работающего с ними.
Вместе с развитием объектно-ориентированного программирования стали развиваться и общие объектно-ориентированные методы разработки ПО. В самом деле, нелепой выглядит ситуация, когда ПО спроектировано с помощью структурного метода [1], а реализовано в объектно-ориентированном стиле. Ведь основной задачей этапов разработки ПО, предшествующих непосредственному программированию, является спецификация предметной области в терминах, удобных для дальнейшего применения в процессе разработки. Таким образом осуществляется перевод информации из того вида, в котором она существует в сознании специалистов предметной области (инженеров-телефонистов - при разработке ПО для телефонной станции, инженеров-железнодорожников - для создания системы автоматизации железной дороги и т.д.) на язык программистов.
В течение последних нескольких лет при поддержке OMG специалистами фирм Rational Software Corporation. и др. разрабатывался Унифицированный Язык Моделирования (UML - Unified Modeling Language, [2]), который предоставляет объектно-ориентированный метод разработки ПО с поддержанием объектно-ориентированной реализации.
Данная работа является обзором нотации диаграмм классов, принятой в рамках последней на данный момент официальной UML версии 1.1, вышедшей в сентябре 1997 года. В работе использовались также материалы предварительной версии OMG/UML 1.3R9 от января 1999 года.
Актуальность данной работы объясняется следующими факторами:
· Модель классов является самой важной частью UML, так как при объектно-ориентированном подходе основной задачей является построение именно диаграмм классов для разрабатываемого ПО, а все остальные диаграммы носят вспомогательный характер;
· Использование диаграмм классов UML как основы для модели классов в методологии RTST++ [3];
· Отсутствие работ на русском языке, подробно описывающих UML;
· Запутанность и непонятность канонического описания UML.
В данной работе приводится краткий и не претендующий на полноту обзор литературы по UML. Далее описываются основные принципы объектно-ориентированного подхода к разработке ПО, приводятся основные фазы жизненного цикла ПО, и коротко рассматриваются все модели UML. После чего идет описание самой модели классов, и в заключении описываются случаи реального эффективного использования модели классов UML, известные авторам.
Обзор литературы по UML
Для того чтобы хорошо ориентироваться в UML, т.е. успешно использовать его на практике, надо иметь довольно глубокие представления о методах объектно-ориентированного анализа, проектирования и программирования. Приведем список литературы по UML с кратким описанием содержания каждой работы.
Первая группа работ - [4], [5], [6] - содержит описание объектно-ориентированных методологий, которые были положены в основу UML.
[5] является описанием методологии OMT. Она вышла в 1991 году и на настоящий момент существует много CASE-средств, в той или иной степени поддерживающих ее. До UML эта методология была одной из самых распространенных.
[4] является, по-видимому, лучшей книгой в этой области. Вышло два ее издания - первое в 1991 году, второе - в 1994. Оба издания переведены на русский язык.
[6] содержит описание OOSE - объектно-ориентированного подхода к созданию программных систем, основанного на модели случаев использования (use case driven approach) и является систематизацией более чем 20-летнего опыта ее автора, Ивара Джекобсона, в области создания больших систем. Эта книга вышла в свет в 1992 году. Модель случаев использования и многое, с ней связанное, вошли в UML.
Вторая группа литературы является каноническим описанием стандарта UML
[2] и [7] являются основными документами по UML. Там описывается метамодель UML и очень мало внимания уделяется семантике конструкций. В [8] описывается пример разработки программной системы (регистрация студентов на посещение учебных курсов) с использованием CASE-средства Rational Rose, реализующего подмножество UML (анализ и проектирование). Все эти документы свободно доступны и их можно взять на web-узле OMG. В данной работе мы также использовали материалы новой предварительной версии UML [9].
Поскольку официальная документация по UML затруднительна для понимания, выходит много книг, описывающих его с разными акцентами. Мы отметим книги, написанные главными авторами UML - Г. Бучем, И Джекобсоном, Д. Рэмбо - [10], [11], [12]:
· [10] - детальная информация об использовании UML. Покрывает около 80% языка. Применение UML описывается на большом количестве примеров;
· [11] - описание процесса объектно-ориентированной разработки ПО;
· [12] - справочник по UML, охватывающий весь язык.
Кроме того, мы отметим еще книгу [13], написанную сотрудником фирмы i-Logix Брюсом Дугласом, в которой содержится хорошее описание UML в контексте разработки систем реального времени.
Еще одним источником информации по UML являются материалы, выпускаемые компанией Rational Software Corp. Это прежде всего огромная база данных RUP, которая содержит более 1000 статей вокруг UML. Кроме того, с декабря 1998 года выходит в свет журнал "Rose Architect", который содержит много интересных статей специалистов фирмы Rational Software Corp. о UML, RUP, Rational Rose, о применениях Rational Rose в различных областях разработки ПО.
Мы постарались сделать этот обзор максимально независимым от перечисленных книг и надеемся, что он поможет разобраться в диаграммах классов UML людям, не имеющим свободного доступа к перечисленной выше англоязычной литературе.
Объектно-ориентированный подход
Основные принципы
Первая и главная идея, лежащая в основе объектно-ориентированного подхода такова: программная система представляется в виде множества самостоятельных сущностей (объектов), взаимодействующих друг с другом. Каждая сущность сама отвечает за хранение информации, необходимой для ее жизни, и, кроме того, она имеет (реализует) свое собственное поведение.
Представим себе систему, состоящую из семьи (отец, ребенок, мать и т.д.) и квартиры (помещения, мебель, техника). Было бы логично выделить в этой системе следующие категории сущностей: человек, предмет мебели, прибор, помещение. Даже ребенок (человек) может включить телевизор (прибор), выбрать понравившийся ему канал, отрегулировать громкость. При этом вся внутренняя структура телевизора от него скрыта: все детали помещены в ящик, снаружи которого есть только органы управления и экран. Ясно, что для правильной работы телевизора состояние внутренних деталей не менее (а возможно даже более) важно, чем состояние ручки изменения громкости. Но мало кто обращает на это внимание: весь внутренний мир телевизора помещен в некоторую оболочку, содержимым которой ведает только телевизор. Так мы замечаем в окружающем мире то, что называется инкапсуляцией - один из основных принципов объектно-ориентированного подхода.
Инкапсуляция - это принцип, заключающийся в построении оболочки вокруг некоторого набора данных и кода, обрабатывающего эти данные.
В случае телевизора можно сказать, что он инкапсулирует свои управляющие устройства, а также радиодетали, обеспечивающие его работу.
При этом запрещается прямой доступ к данным извне. Некоторый внешний объект может повлиять на инкапсулируемые данные только так, как позволяет объект, этими данными владеющий. В примере с телевизором ребенок может изменять выбранный канал, громкость, яркость (с помощью ручек управления), но не может менять напряжение на участках электрической цепи.
Для описания следующего принципа, лежащего в основе ООП, надо определить, что такое класс.
Как уже говорилось, разрабатываемая система представляется в виде набора объектов. В частности, объектами являются "Телевизор Горизонт-ТЦ375И No. 1234567", "Телевизор Янтарь No. 7654321". В некоторый момент бывает удобно сослаться не на конкретный телевизор, а на все телевизоры, независимо от их конкретной модели, года выпуска и серийного номера. В этом случае нам поможет понятие класса.
Класс- это группа сущностей (объектов), обладающих сходными свойствами, а именно, данными и поведением [4].
В дальнейшем отдельного представителя некоторого класса будем называть объектом класса или просто объектом.
Под поведением объекта в UML понимаются любые правила взаимодействия объекта с внешним миром и с данными самого объекта.
В нашем примере можно выделить следующие классы и их представителей:
Класс
Представители
Человек
Михаил Сергеевич, Раиса Максимовна, Петя
Помещение
Гостиная, спальня, кабинет, кухня, ванная, туалет, балкон, кладовка
Прибор
Телевизор Янтарь No. 7654321, утюг, пылесос, плита, холодильник, компьютер, водопроводный кран, раковина
Для того чтобы как можно более гибко разделить сущности на классы и предоставить возможность в разное время смотреть на одну и ту же сущность на разном уровне абстракции, было введено понятие наследования.
Наследование - это отношение типа "является разновидностью". В контексте рассматриваемого примера можно сказать, что Электроприбор "является разновидностью" Прибора, а Телевизор "является разновидностью" Устройства Отображения. Фактически, мы говорим, что один класс является наследником другого, если класс имеет все те свойства, что и предок, плюс еще некоторые дополнительные.
В случае с приборами из нашего примера можно построить такую иерархию наследования:
Предок
Потомки
Прибор
Электроприбор, сантехнический прибор
Электроприбор
Нагревательный прибор, устройство отображения, звуковоспроизводящий прибор
Устройство отображения
Телевизор, монитор
Третий принцип, лежащий в основании ООП, - полиморфизм - касается аспектов определения поведения объектов классов и распространения поведения вдоль иерархии наследования от предков к потомкам.
В UML для описания полиморфизма вводятся понятия операции и метода. У классов есть операции, которые определяют его поведение. В некотором смысле операция - это набор общих сведений о поведении класса: детали реализации никак не специфицированы операцией, но некоторый комментарий по поводу реализации может быть дан в неформальном виде, например, на естественном языке. При этом каждый потомок класса может предоставить метод, реализующий любую унаследованную операцию, отличный от соответствующего метода предка. Подчеркнем, что операция - это лишь описание какой-либо черты поведения объекта, а метод - уже конкретная реализация. Операции обязательно наследуются, т.е. распространяются вдоль иерархии без каких-либо изменений, а методы могут перекрываться потомками для реализации конкретных деталей поведения, присущих объектам класса-потомка.
Возвращаясь к приведенному примеру, можно сказать, что класс Устройство Отображения имеет операцию Принять Сигнал, которая лишь обозначает возможность получения объектом класса Устройство Отображения (или производного от него) некоторого сигнала извне. Вместе с тем у класса Телевизор будет метод Принять Сигнал, принимающий сигнал с антенного кабеля, а у класса Монитор будет метод Принять Сигнал, принимающий сигнал от схемы видеоадаптера.
Еще один принцип ООП - это модульность [4], и это означает, что вся система должна быть разделена на части, называемые модулями. Это деление более крупное, чем разбиение на классы - модули должны их в себе содержать.
Стадии разработки ПО
В [4] так определяются этапы объектно-ориентированного подхода к разработке ПО:
Объектно-ориентированный анализ (analysis) - способ анализа, изучающий требования к системе с точки зрения будущих классов и объектов, основываясь на словаре предметной области.
Объектно-ориентированное проектирование (design) - способ проектирования, включающий в себя описание процесса объектно-ориентированной декомпозиции и объектно-ориентированную нотацию для описания различных моделей системы (логической и физической, статической и динамической).
Объектно-ориентированное программирование - это метод реализации, в основе которого лежит идея представления программной системы в виде набора взаимодействующих объектов, каждый из которых является экземпляром некоторого класса, а классы объединены в иерархию наследования.
Порядок их применения таков: сначала проводится объектно-ориентированный анализ, затем проектирование, а после этого - реализация (то есть программирование).
Кроме перечисленных этапов, принято еще выделять этап тестирования и отладки, следующий за реализацией и этап сопровождения, который в большинстве случаев является самым долговременным и дорогостоящим.
Диаграммы классов в UML
UML имеет четырехуровневую архитектуру:
· Мета-метамодель;
· Метамодель;
· Модель;
· Пользовательские объекты.
Пользовательские объекты определяют объекты конкретной предметной области, например: телевизор, монитор.
Модель является определенным взглядом на предметную область [4].
В UML существуют следующие модели:
· Модель случаев использования (use case model). Предназначена для описания требований к системе и подсистемам;
· Модель классов (class model). Служит для описания статической структуры системы: иерархии классов и отношений между ними;
· Модель взаимодействий - объекты (collaboration model) и сценарии (sequence model). Служит для описания механизмов взаимодействия объектов системы, реализующих ту или иную функцию;
· Поведенческая модель диаграммы переходов и состояний (behaviour model). Предназначены для описания алгоритмов поведения объектов системы.
· Модель процессов - физическая архитектура системы (deployment model). Описывают распределение процессов по процессорам в физическом проекте системы;
· Модель программных модулей (component model). Описывают распределение классов и объектов системы по модулям в физическом проекте системы.
· Модель действий (activity model). Предназдначена для описания алгоритмом системы (для методов классов, для нескольких классов) и являются вариантом поведенческой модели без сообщений.
Метамодель определяет язык описания моделей. В UML метамодель описывается с помощью диаграмм классов UML.
Мета-метамодель является описанием различных метамоделей.На уровне мета-метамодели рассматривается классификация подходов разработки программного обеспечения (ПО). Самыми распространенными являются два семейства методов: структурные методы проектирования программных систем и объектно-ориентированные, приобретающие все большую популярность в последние годы.
В этой работе речь пойдет о метамодели UML, а вернее, о той ее части, которая служит для описания диаграмм классов.
Диаграмма классов UML основана на широко распространенной модели "сущность-связь" (ERD - Entity Relationship Diagram), введенной П.Ченом [1]. Нотация ERD получила и другие варианты развития. Например, CASE-метод Баркера [15], метод IDEF0 [16], который лежит в основе широко известного CASE-средства ERWin.
Диаграмма классов UML - это граф, узлами которого являются элементы статической структуры проекта (классы, интерфейсы, шаблоны классов), а дугами - отношения между узлами (ассоциации, наследование, зависимости). На диаграмме классов изображаются следующие элементы:
· Пакет (package) - набор элементов модели, логически связанных между собой;
· Класс (class) - описание общих свойств группы сходных объектов;
· Интерфейс (interface) - абстрактный класс, задающий набор операций, которые объект произвольного класса, связанного с данным интерфейсом, предоставляет другим объектам;
· Шаблон (template) или параметризованный класс (parameterized class). Шаблоны UML очень похожи на шаблоны C++. Они определяют семейство классов, отличающихся значением некоторых формальных параметров;
· Утилита (utility) - класс, объединяющий группу общедоступных (глобальных) переменных и процедур;
· Метакласс (metaclass) - средство для классификации классов: экземплярами метакласса являются классы. Таким образом, метакласс является элементом абстракции более высокого уровня, чем класс;
· Объект (object) - экземпляр класса;
· Ассоциация (association) - отношение между классами, являющееся cпособом описания взаимодействия объектов этих классов;
· Наследование (inheritance) - отношение между классами, с помощью которого можно в отдельный класс вынести общие свойства нескольких классов. Это отношение аналогично наследованию в объектно-ориентированных языках программирования;
· Зависимость (dependency) между элементами модели. Например, между классом и интерфейсом может быть установлена зависимость "uses", означающая, что класс использует интерфейс.
Рассмотрим теперь подробнее элементы диаграммы классов.
Пакеты
В контексте диаграмм классов, пакет - это вместилище для некоторого набора классов и других пакетов. Пакет является самостоятельным пространством имен.
В UML нет каких-либо ограничений на правила, по которым разработчики могут или должны группировать классы в пакеты. Но есть некоторые стандартные случаи, когда такая группировка уместна, например, тесно взаимодействующие классы, или более общий случай - разбиение системы на подсистемы.
Отметим, что пакет физически содержит сущности, определенные в нем (говорят, что "сущности принадлежат пакету"). Это означает, что если будет уничтожен пакет, то будут уничтожено и все его содержимое.
При описании классов пакета нередко бывает полезно сослаться на класс, определенный в другом пакете. Это можно сделать, импортировав нужный пакет. Это означает, что в импортирующем пакете станут доступными все классы импортируемого пакета. При этом пространства имен не объединятся: для использования класса надо будет указать его имя с полным путем пакета, в котором он лежит.
Класс
Общее понятие класса в UML
Класс - это сущность, описывающая множество объектов со сходной структурой, поведением и связями с другими объектами.
В UML существует несколько разновидностей класса: интерфейс, утилита и др. Все они похожи, но есть и отличия: интерфейс - это класс, в котором определены только операции, а утилита - это класс, все атрибуты и операции которого общедоступны (public). Для указания вида класса в UML введено понятие стереотипа (stereotype). Соответственно, классы-интерфейсы имеют стереотип "interface", а классы-утилиты - "utility". Существует стандартный набор стереотипов, который, при необходимости, можно расширять.
На диаграммах класс изображается в виде прямоугольника со сплошной границей, разделенного горизонтальными линиями на 3 секции:
Верхняя секция (секция имени) содержит имя класса и другие общие свойства (в частности, стереотип). В средней секции содержится список атрибутов (членов-данных), а в нижней - список операций (функций-членов). Атрибуты хранят инкапсулированные данные класса, а операции описывают поведение объектов класса. Другой взгляд на поведение и данные класса - это его отношения с другими классами (ассоциации, наследование и др.).
Любая из секций атрибутов и операций может не изображаться (а также обе сразу). Для отсутствующей секции не нужно рисовать разделительную линию и как-либо указывать на наличие или отсутствие элементов в ней.
На усмотрение конкретной реализации могут быть введены дополнительные секции, например, исключения (Exceptions).
Область видимости класса - это пакет, в котором он описан. Это означает, что имена классов должны быть уникальны среди имен классов того же пакета.
По умолчанию считается, что указываемый класс определен в текущем пакете. Если надо сослаться на класс из другого пакета, это указывается явно:
Имя_пакета::Имя_класса
Так как иерархия пакетов может иметь глубину вложенности большую, чем 1, то путь к классу может содержать более чем один пакет, при этом путь начинается от корня иерархии пакетов:
Имя_пакета1::Имя_пакета2::...::Имя_пакетаN::Имя_класса
В секции имени класса могут находиться (по порядку сверху вниз):
· Стереотип класса (и/или иконка стереотипа в правом верхнем углу) - необязательное поле, опускается, если речь идет о неспецифицированном классе;
· Имя класса (если класс абстрактный - курсивом);
· Дополнительные свойства: имя автора и т.п. (не обязательное поле).
Средняя и нижняя секции прямоугольника класса содержат списки его атрибутов и операций. Элементы этих списков можно группировать по некоторым признакам. В этом случае перед группой элементов ставится заключенная в кавычки строка, определяющая свойство, причем это свойство распространяется на все нижестоящие элементы до нового свойства. Эта возможность хорошо иллюстрируется следующим примером:
"constructors"
CRect(CPoint left_up, CPoint right_down)
CRect(left:Integer, top:Integer, right:Integer, bottom:Integer)
"Data access"
GetLeftUp(): CPoint
SetLeftUp(CPoint lup)
GetRightDown():CPoint
SetRightDown (CPoint rdn)
...
В одном представлении списка элемент списка должен присутствовать не более одного раза, хотя в разных представлениях можно задавать различную группировку. Например, список, приведенный выше, может быть представлен следующим образом:
"Get-Data functions"
GetLeftUp(): CPoint
SetLeftUp(CPoint lup)
"Set-Data functions"
GetRightDown():CPoint
SetRightDown (CPoint rdn)
Отметим, что место элемента в списке задается при добавлении его в список и никак не зависит от группировки элементов в представлениях. Кроме того, в некоторое представление списка какие-то его элементы могут не попасть (например, в случае установки фильтра или при ограничении количества видимых элементов). В этом случае вместо этих элементов в списке ставится три точки.
У каждой секции прямоугольника класса может быть имя. При этом, так как секция "Имя класса" обязательна, то ее имя не указывается.
Атрибут
Атрибут (attribute) - это инкапсулируемый элемент данных класса, т.е. элемент данных, который содержится в объекте, принадлежащем описываемому классу.
У атрибута должен быть тип (type expression), который может представлять собой простой тип или быть сложным, как например:
CArray<CString *, CPoint *>
В любом случае, детали, касающиеся типов атрибутов, не специфицированы UML. Единственное, что определено - это указание имени одного из классов системы. Более подробное описание типа зависит от того, какой язык программирования используется разработчиками.
Атрибут изображается в виде текстовой строки, отражающей различные его свойства:
<видимость> <имя>:<тип> = <начальное_значение> {<свойства>}
<видимость> имеет С++ семантику видимости членов класса:
· Открытый атрибут (public), обозначается символом +;
· Защищенный атрибут (protected), обозначается символом #;
· Закрытый атрибут (private), обозначается символом -.
Видимость типа "открытый атрибут" означает, что любая сущность, имеющая доступ к объекту определяемого класса, имеет доступ и к этому атрибуту.
Видимость типа "защищенный атрибут" означает, что к атрибуту имеют доступ только методы данного класса и его потомков.
Видимость типа "закрытый атрибут" означает, что атрибут доступен только методам класса, который его инкапсулирует.
Символ области видимости может быть опущен. Это означает, что область видимости не показывается (а не то, что она не определена или "public" по умолчанию). Также область видимости может изображаться ключевым словом "public", "private" или "protected". Это особенно удобно для задания области видимости для целой группы атрибутов:
"public"
xCoord : integer = 0
yCoord : integer = 0
"private"
rgbColor : RGB = (192, 192, 192)
<имя> - это идентификатор, представляющий имя атрибута.
<тип> - зависящее от языка реализации описание типа атрибута.
<начальное_значение> - зависящее от языка реализации выражение, задающее начальное значение для атрибута вновь созданного объекта. Эта часть определения атрибута не обязательна. Независимо от ее наличия конструктор объекта может изменить начальное значение.
<свойства> - строка дополнительных свойств элемента. Необязательная часть. Если свойства не указываются, скобки {} опускаются. Примером свойства может служить имя автора:
{Author = Smith}
Нет символа, показывающего возможность изменить атрибут. По умолчанию атрибут изменяемый. Указав в его свойствах пометку "{frozen}" можно объявить атрибут неизменяемым.
Для атрибута можно указывать его множественность. Если она не обозначена, то предполагается, что атрибут может хранить ровно одно значение. Множественность может быть определена в квадратных скобках сразу после имени атрибута:
coords[3]: integer
sons[0..*]: human
Пример:
+color: RGB = (192, 192, 192)
#navigable: boolean = TRUE
+goal: enum(gTest, gWork) = gWork
-id: integer
Операция
Операция (operation) - это сущность, определяющая некоторое действие, которое может быть выполнено представителем класса. У операции есть имя и список аргументов.
Операция изображается текстовой строкой, имеющей следующую грамматику:
<видимость><имя>(<список_параметров>):<тип_возвращаемого_значения> {<свойства>}
<видимость> и <имя> имеют тот же смысл, что и для атрибута.
<тип_возвращаемого_значения> - зависящее от языка реализации описание типа значения, возвращаемого функцией. Если оно не указано, то предполагается, что функция не возвращает значения (void для C/C++).
<список_параметров> - список формальных параметров, разделенных запятыми:
<вид> <имя>:<тип> = <значение_по_умолчанию>
<вид> - одно из in (входной параметр), out (выходной параметр), или inout (смешанный, т.е. и входной и выходной), по умолчанию in.
Если параметр помечен как входной, то функция не может изменять его значения, но может его использовать (обычные параметры C/C++); если параметр помечен как выходной, то функция не может использовать его значение, но может присвоить ему некоторое значение. Наконец, если параметр помечен как смешанный, то функция может и читать его значение, и изменять (параметры-переменные, или параметры, передающиеся по ссылке).
Все операции, определенные в классе, можно разделить на две группы: операции класса и операции представителя. Операции класса - это операции, присущие не объектам класса, а самому классу. Отсюда, в частности, следует, что операции класса не имеют доступа к атрибутам. Типичный пример операции класса - функция создания нового объекта (представителя) класса. Операции класса выделяются подчеркиванием:
createObject(void): PObject
Операция, не изменяющая состояние системы, помечается следующим образом: в список свойств операции помещается свойство {query}.
Импорт пакета
В заключение рассказа о классах приведем примеры изображения пакетов на диаграммах классов - использование класса из другого пакета (т.е. не текущего) (рис.3) и импортирование класса (рис.4).
На рис. 4 показано, что пакет с именем Current импортирует пакет с именем java::awt. На рис. 3, являющемся фрагментом диаграммы классов пакета Current, показано использование класса, принадлежащего другому пакету: класс MyApplet, определенный в пакете Current, наследуется от класса java::awt::Applet, определенного в пакете java::awt. Напомним, что строка java::awt::Applet задает полный путь класса от корня иерархии пакетов и ссылается на класс Applet в пакете awt, который, в свою очередь, принадлежит пакету верхнего уровня java.
Интерфейс
Когда с помощью объектно-ориентированного подхода начали разрабатывать крупные программные системы, выяснилось, что кроме классов нужны дополнительные уровни абстракции. В частности, если имеется некий сложный объект, функциональность которого проявляется по-разному, в зависимости от того, с кем он взаимодействует, бывает удобно скрыть все функции, не нужные в данный момент. А точнее: на время данного взаимодействия сделать доступными все необходимые функции и только их.
Простейший случай использования такого подхода показан на следующем примере.
Все элементы управления телевизора можно разделить на несколько групп: пользовательские (громкость, номер канала), специальные (частота канала) и аппаратные (параметры электрических цепей). Ясно, что пользователь работает с пользовательскими органами управления, настройщик - со специальными, а телемастер - с аппаратными. При этом, если телевизор исправен и настроен, то пользователю нет необходимости видеть и менять состояние специальных и аппаратных органов управления. Поэтому пользовательские элементы управления обычно выносятся на переднюю панель телевизора, специальные закрыты небольшой дверцей, а аппаратные вообще погружены внутрь корпуса. Ясно, что если бы все было на поверхности, пользователь мог бы сделать все то же, что и раньше, но ему бы оказались доступны специальные и аппаратные органы управления, и он мог бы случайно испортить настройки. Кроме того, передняя панель была бы загромождена настолько, что мало кто смог бы ориентироваться в обилии кнопок, ручек и т.п.
Для описания таких групп функций были разработаны интерфейсы (interface).
Интерфейс в UML - это полностью абстрактный класс (это означает, что не могут быть созданы экземпляры этого класса), не имеющий собственных данных.
Фактически, интерфейс - это описание (без реализации) группы функций, которые один класс предоставляет для использования другому классу. Логика работы этих функций не определяется. Имеется лишь возможность задать неформальное (например, на естественном языке) описание того, что от них требуется.
Между двумя интерфейсами можно задать отношение наследования. Оно будет означать обычное теоретико-множественное объединение списков операций предка и потомка.
Будем говорить, что класс поддерживает (или реализует) интерфейс, если он содержит методы, реализующие все операции интерфейса.
На диаграмме классов UML интерфейс можно изобразить двумя способами: развернутым и сокращенным.
В случае развернутого способа интерфейс изображается на диаграмме как класс со стереотипом "interface" и без секции атрибутов.
На рис. 5 изображен класс RunningLine, который реализует интерфейс DataConsumer. Таким образом, класс RunningLine должен предоставить метод, реализующий операцию SetData, унаследованную от интерфейса DataConsumer.
На рис. 6 изображен класс RunningLine, использующий интерфейс DataConsumer.
Как упоминалось выше, допустимо сокращенное изображение интерфейса - небольшой кружок с именем интерфейса возле него.
Типы и классы реализации
Представим себе, что некий объект системы в разное время ведет себя по-разному. Пример придумать легко: вполне конкретное вещество, скажем, вода, может пребывать в одном из трех агрегатных состояний: твердом, жидком и газообразном. При этом вещество-то остается одним и тем же, но вода не может растаять, а лед может, лед не может замерзнуть, а вода может. Для того чтобы корректно описывались такие ситуации, в UML ввели понятие типа (type). Классы в обычном понимании (равно как и в терминологии C++) в UML называются классами реализации (implementation class). Они задают структуру объектов на уровне реализации. Именно в форме, определяемой классом реализации, объект будет храниться в памяти системы, и, если потребуется, в базе данных. При этом один класс реализации может поддерживать несколько типов (и наоборот, один тип могут поддерживать разные классы реализации).
На первый взгляд может показаться, что интерфейс и тип - это одно и то же. Но это не верно. Между ними есть одно очень важное отличие: класс, поддерживающий интерфейс, поддерживает его всегда, то есть любой его представитель должен быть постоянно готов к исполнению всех операций, заданных во всех интерфейсах, поддерживаемых им. В то же время объект в процессе функционирования системы не должен постоянно поддерживать все типы. Верно лишь следующее: если класс поддерживает тип, то объекты этого класса в некоторые моменты своей жизни могут поддерживать этот тип.
Классы реализации и типы изображаются прямоугольником со стереотипом "implementation class" и "type" соответственно. Если класс реализации поддерживает некоторый тип, то они соединяются пунктирной линией с полым треугольником со стороны типа (см. рис.7).
Ассоциация
Ассоциация определяет некоторую связь между классами. Когда в системе будут созданы представители ассоциированных классов, они будут связаны так, как определяет рассматриваемая ассоциация.
В UML одна ассоциация может специфицировать связь между несколькими (возможно, более чем двумя) классами. Такие ассоциации называются N-арными. Мы рассмотрим самый распространенный случай - бинарную ассоциацию.
Бинарная ассоциация
Бинарная ассоциация (binary association) - это ассоциация между ровно двумя классами. Возможна ассоциация класса с самим собой, которая называется рефлексивной ассоциацией.
Изображается ассоциация в виде сплошной ломаной линии, соединяющей два символа класса.
Конец ассоциации (т.е. место ее присоединения к классу) называется ролью (association role). Большая часть информации, касающейся ассоциации, присоединена к ее ролям.
На линии (рядом с линией), изображающей ассоциацию, могут быть следующие пометки:
· <имя ассоциации> - определяет необязательное имя ассоциации;
· <символ класса ассоциации> - класс, уточняющий свойства данной ассоциации (соединяется с линией ассоциации пунктирной линией).
Роль
Роль (association role) - это неотделимая часть ассоциации, описывающая некоторые свойства её "соединения" с классом (роль класса в данной ассоциации).
У роли могут быть следующие свойства:
Имя роли - строка, стоящая рядом с концом ассоциации. Поле не обязательное, но если имя задано, оно должно отображаться на диаграмме;
Множественность (multiplicity) - количество представителей (конкретных объектов), которые могут быть связаны с одним партнером ассоциации;
Упорядочение - если множественность больше одного, то множество связанных объектов может быть упорядочено. Варианты:
unordered - неупорядочено (по умолчанию);
ordered - упорядочено (задан линейный порядок) задается пометкой {ordered};
Квалификатор (qualifier) - список атрибутов класса c противоположного конца ассоциации, по значениям которых можно однозначно разбить множество его объектов на подмножества. Используется для связи объекта класса-партнера ассоциации с группой объектов другого класса-партнера ассоциации;
Навигация (navigability) - если в направлении, соответствующим роли, поддерживается навигация, на конце линии может быть изображена стрелка. Возможность навигации в направлении роли означает, что партнеры ассоциации могут просматривать объекты, соответствующие этой роли;
Агрегирование (aggregation) - показывает, что ассоциация является отношением типа целое/часть;
Спецификатор интерфейса - имя типа или интерфейса в следующей форме:
':' <имя>
Это свойство указывает поведение, ожидаемое от объектов ассоциированного класса. Другими словами, спецификатор интерфейса задает поведение, необходимое для удовлетворения требований ассоциации. Реальные классы, как правило, обеспечивают большую функциональность, чем требуется партнерам ассоциации. Указав интерфейс или тип партнера, можно ограничить доступ к его свойствам рамками указанного интерфейса или типа. Если спецификатор интерфейса не указан, то партнеры имеют доступ ко всей функциональности класса;
Изменяемость - если связь изменяема, то есть может быть добавлена, удалена и перемещена, то никаких дополнительных пометок не нужно. В противном случае в строке свойств могут присутствовать:
{frozen} - связи не могут добавляться, удаляться и перемещаться;
ordered - упорядочено (задан линейный порядок) задается пометкой {ordered};
Множественность
Данное свойство показывает возможное количество объектов, которые могут быть связаны в соответствии с этой ассоциацией. Множественность указывается для ролей ассоциации и имеет следующий формат:
<нижняя граница>..<верхняя граница>[,<нижняя граница>..<верхняя граница>]*
<нижняя граница> и <верхняя граница> - это целые числа, или символ '*' для верхней границы, показывающий, что она (верхняя граница) бесконечна.
Примеры:
0..1
10
0..*
3..5,10..20,100,200..*
Ассоциация, изображенная на рис. 8, показывает, что в одной школе могут учиться один или больше (1..*) учеников, а каждый ученик учится только в одной школе.
Квалификатор
В работающей системе между объектами ассоциированных классов будут установлены связи (экземпляры ассоциации). Но в некоторых случаях требуется, чтобы можно было разбить множество объектов одного класса, которые будут связаны с объектом другого класса в соответствии с данной ассоциации, на подмножества по значениям некоторых атрибутов этих объектов, и наложить ограничения на число объектов в том или ином подмножестве.
Например, объект класса "деканат", как правило, не рассматривает каждый объект класса "студент" отдельно, а имеет дело с группой студентов (объектов класса "студент"). Поэтому множество всех студентов разбивается на подмножества. Атрибут, который является критерием этого разбиения, есть номер группы, в которой обучается студент (см. рис. 9). Причем в группах число студентов не должно превышать 20 человек, но в то же время там должен быть хотя бы один человек. Объект класса "деканат" может сделать запрос на студентов из групп №3 и №4, и при этом 3 и 4 являются конкретными значениями атрибута "номер группы".
В UML предоставляется такая возможность: у ассоциации может быть атрибут под названием квалификатор, который содержит один или несколько атрибутов класса, прикрепленного к другому концу ассоциации. Именно по значению этих атрибутов происходит групповая выборка объектов этого класса со стороны объекта противоположного (по данной ассоциации) класса.
Вернемся к нашему примеру. Пусть объект класса "деканат" должен послать сообщение всем студентам группы № 3. В случае отсутствия квалификатора у ассоциации он (деканат) вынужден был бы перебрать всех студентов, и в случае, если очередной студент принадлежит группе № 3, то послать ему сообщение. При наличии квалификатора деканат может послать сообщение всей группе № 3, не перебирая при этом всех студентов.
Квалификатор изображается в виде маленького прямоугольника, присоединенного к началу ассоциации. В нем указываются атрибуты другого класса-партнера ассоциации.
Класс ассоциации
Такой класс является обычным классом, присоединенным пунктирной линией к ассоциации. Его имя должно совпадать с именем ассоциации. Содержащаяся в нем информация уточняет и дополняет сведения об ассоциации.
Агрегирование
Если у роли ассоциации установлен атрибут "агрегирование", то вся ассоциация является отношением агрегирования. Такой атрибут может быть установлен только у одной из ролей.
Агрегирование (aggregation) - это отношение между классами типа целое/часть. Агрегируемый класс в той или иной форме является частью агрегата. На практике это может быть реализовано по-разному. Например, объект класса-агрегата может хранить объект агрегируемого класса, или хранить ссылку на него.
В UML допускается, что один класс агрегируется многими. При этом возможно два варианта. Первый - на диаграмме классов такая множественность означает лишь возможность для соответствующих объектов - т.е. агрегируемый объект может агрегироваться любым объектом, который принадлежит классам, агрегирующим класс данного объекта. Но объект не агрегируется многими объектами. Второй вариант - объект все-таки может агрегироваться несколькими объектами - так называемое каталожное агрегирование. Примером такого агрегирования может быть книга в библиотеке, которая принадлежит сразу многим каталогам.
По умолчанию агрегирование в UML может быть каталожным. Но имеется специальный вид агрегирования, называемый композицией, который этого не допускает.
Композиция - это специальный вид агрегирования (так называемое сильное агрегирование). В частности, агрегируемый объект может быть создан только тогда, когда создан агрегат, а с уничтожением агрегата уничтожаются и все агрегируемые объекты. Агрегируемый объект не разделяем, т.е. владеть им может только один агрегат.
Например, в ROOM существует еще более сильное агрегирование - там объекты, находящиеся внутри агрегата, невидны с наружи и сами не видят окружения своего агрегата (т.е. с ними нельзя устанавливать связи, им нельзя напрямую посылать сообщения и т.д.) и общаются с ним только через специальные точки входа - порты. В каждой предметной области, в каждом проекте у отношения агрегирования могут быть свои специфические свойства, которые можно реализовать как параметры кодогенерации для отношения агрегирования (такие поля для разных элементов модели имеются в Rose Rational - одной из самых известных реализаций UML).
Агрегирование изображается на диаграмме полым ромбом на конце линии ассоциации со стороны агрегирующего класса (агрегата).
Композиция показывается также как и агрегирование, но ромбик рисуется не пустым, а заполненным.
Кроме того, UML допускает "вложенную" нотацию, в которой агрегируемый класс изображается внутри класса-агрегата. При этом перед его именем указывается имя роли и символ ':'.
Наследование
Наследование - это отношение типа общее-частное между элементами модели.
Наследование пакетов означает, что в пакете-наследнике все сущности пакета-предка будут видны под своими собственными именами (т.е. пространства имен объединяются). На практике наследование пакетов применяется достаточно редко (в Rational Rose (классическая реализация UML) такая возможность не поддерживается).
Наследование показывается сплошной линией, идущей от конкретного элемента к более общему (в терминологии ООП - от потомка к предку, от сына к отцу, или от подкласса к суперклассу). Со стороны более общего элемента рисуется большой полый треугольник.
Один из атрибутов отношения наследования - дискриминатор (discriminator) -строка, задающая имя группы потомков [5], [2]. Его использование полезно, если у данного класса много потомков, и мы хотим разбить их на несколько групп. Отсутствие дискриминатора означает, что дискриминатор - пустая строка (дискриминатор по умолчанию). Изображается дискриминатор текстовой строкой, стоящей возле линии наследования.
Способ изображения наследования показан на следующем рисунке:
На рис. 12 geometry - это дискриминатор, а {disjoint} - его дополнительное свойство, на описании которого мы остановимся ниже.
Остановимся на понятии множественного наследования. Возвращаясь к примеру, с которого мы начинали эту статью, можно сказать, что Телевизор (как класс, а не как отдельный представитель) наследуется от классов Деревянный_Ящик и Устройство_Отображения (см. рисунок 14). При этом класс Устройство_Отображения инкапсулирует стандартную электронную начинку, а класс Деревянный_Ящик - только физическую оболочку. Во многих современных языках программирования множественное наследование запрещено. Связано это со следующей проблемой. Представим себе, что класс A наследуется от классов B и C, а классы B и C, в свою очередь, имеют общего предка D (см. рис. 13). Такая ситуация называется перекрывающимся наследованием. Какой из экземпляров D будет храниться в экземпляре A? Для решения этой проблемы была разработана концепция виртуального наследования, обеспечивающая единственность экземпляра класса D в памяти. Но ее использование небезопасно и нетривиально.
Для того чтобы уточнить подробности, касающиеся множественного наследования, на дискриминатор могут накладываться дополнительные ограничения (constraint):
· disjoint - запрещение множественного наследования;
· overlapping - разрешение множественного наследования.
В контексте рисунка 12 первое свойство означает, что нельзя в множественном наследовании использовать двух потомков класса figure, если они произведены от него через ветку наследования, помеченную дискриминатором geometry. (На рисунке 15 показана неправильна ошибочная ситуация.) Если бы этот дискриминатор имел свойство {overlapping}, то это было бы возможно.(На рисунке 13 показана ситуация, невозможная для наследования со свойством {disjoint}, но возможная для случая {overlapping}.) По умолчанию дискриминатор имеет свойство {overlapping}
Для дискриминатора можно указать дополнительные ограничения, не касающиеся множественного наследования:
· Complete (полный) - все потомки рассматриваемого класса определены (возможно, не показаны на данной диаграмме), и список потомков не может быть расширен;
· Incomplete (неполный) - возможно появление новых классов-потомков.
В примере на рис. 16 запрещено наследование новых классов от класса CHttpServer:
Параметризованные классы (шаблоны)
В некоторых случаях в модели необходимы классы со схожей структурой, которые отличаются некоторыми параметрами. Например, имеется описание нескольких динамических массивов для элементов разных типов, а многие операции над их элементами совпадают. Тогда было бы целесообразно определить такую структуру данных, чтобы с ее помощью было бы легко получить те самые динамические массивы, и делать это можно было бы уточнением параметров. Для этого в UML вводится понятие параметризованных классов, которые еще называют шаблонами.
Параметризованный класс или шаблон - это описание множества классов с одним или более неопределенным формальным параметром, то есть это семейство классов, где каждый класс отличается значениями этих неопределенных параметров.
Таким образом, шаблон определяется в терминах формальных параметров и, следовательно, его нельзя использовать напрямую как обычный класс, так как его параметры должны быть привязаны к определенным значениям.
Шаблон не может участвовать в большинстве обычных отношений между классами. Существует всего два вида отношений, в которых он может участвовать - связи между шаблоном и классом, порожденным от него подстановкой параметров (помечается ключевым словом "bind"), и направленные ассоциации. Направленная ассоциация должна идти в направлении от шаблона (то есть навигация в направлении от шаблона).
На рис. 17 изображен шаблон TList (список, состоящий из k элементов типа C) и экземпляр этого шаблона RecordList, который получается подстановкой класса Record вместо формального параметра C, и присваиванием параметру k значения 30. На том же рисунке показано и сокращенное обозначение привязки конкретных значений формальным параметрам: TList<Rectangle, 6>. Здесь в угловых скобках по порядку перечислены фактические параметры: класс Rectangle и целое число 6.
Отметим, что объявление новых атрибутов и операций в экземпляре шаблона невозможно. В связи с этим операции и атрибуты экземпляров шаблонов не отображаются на диаграмме. Но иногда требуется добавить новые свойства в класс, и в таких случаях следует создать новый класс, чьим предком будет экземпляр шаблона, и далее добавить нужные операции и атрибуты.
Утилиты
В некоторых случаях при описании классов приходится часто пользоваться некоторыми глобальными функциями и переменными. Поэтому для удобства программирования введено такое понятие как утилита, где собираются все такие функции и переменные. Этой сущности в UML дан статус класса специального вида.
На диаграмме утилита изображается как класс со стереотипом "utility", и может иметь как атрибуты, так и операции.
Объект
Одним из самых важных понятий объектно-ориентированного программирования является понятие объект. Объект есть экземпляр класса, в некоторых специальных случаях он может быть экземпляром нескольких классов (на этом остановимся ниже). Объект обладает набором состояний, в которых он может находиться, строго определенным поведением и уникальным идентификатором; структура и поведение схожих объектов определяется в их общем классе.
Объекты могут исполнять определенные роли. Роль определяет отношение между классом и его экземплярами [4], выделяя определенное их подмножество. Считается, что все эти объекты похожи по своему поведению и состояниям, которые они могут принимать. Например, в системе может существовать много объектов класса Абонент, но часть из них в данный момент играет роль вызываемых, часть - вызывающих, а остальные свободны. Таким образом у этого класса мы выделили три роли.
Таким образом, в UML существует два понятия роли: роль у партнеров ассоциации и роль, которую могут исполнять объекты. В дальнейшем во избежание путаницы будем называть роль, исполняемую объектом, объектом-ролью.
Фактически, объект-роль - это некоторый промежуточный уровень абстракции между объектами и классами. Например, рассмотрим класс "человек": экземпляром этого класса является конкретный человек с именем и фамилией (его уникальным идентификатором) - Иванов Иван Петрович. У этого экземпляра могут быть состояния, например, "болен", "здоров", "спит" и т.п. В качестве объекта-роли можно привести роли: "сын", "жена", "муж". Таким образом, объект-экземпляр класса может исполнять разные роли, например роли "мужа" и "сына".
У объекта и объекта-роли есть уникальный идентификатор и строго определенные значения атрибутов.
На диаграмме объект и объект-роль представляется как прямоугольник с двумя отделениями. Верхнее отделение содержит имя объекта и имя его класса, по следующему синтаксису:
<Имя объекта>: <имя класса>
Подобные документы
Объектно-ориентированный подход к проектированию программных систем. Простое наследование и доступ к наследуемым компонентам. Конструкторы производных классов, объемлющие классы, понятие об алгоритме и операторе. Примеры реализации связных списков.
реферат [24,5 K], добавлен 31.10.2011Основные понятия объектно-ориентированного программирования в PHP5. Структурный и объектно-ориентированный подход. Класс как абстрактный тип. Реализация класса. Конструкторы и деструкторы. Функция l_visited_style изменение стиля посещенных ссылок.
курсовая работа [433,2 K], добавлен 13.06.2008Объектно-ориентированный язык программирования: общая характеристика и свойства. Базовый и производный классы, конструкторы производного класса. Конструкторы и неопределенность при множественном наследовании. Роль наследования при разработке программ.
курсовая работа [688,3 K], добавлен 23.12.2013Основные принципы дизайна классов в объектно-ориентированном проектировании: единственной обязанности, открытости-закрытости, подстановки Барбары Лизков, разделения интерфейса, инверсии зависимостей. Понятие быстрой разработки программ Мартина Роберта.
презентация [57,5 K], добавлен 05.01.2014Анализ объектно-ориентированного программирования, имитирующего способы выполнения предметов. Основные принципы объектно-ориентированного программирования: инкапсуляция, наследование, полиморфизм. Понятие классов, полей, методов, сообщений, событий.
контрольная работа [51,7 K], добавлен 22.01.2013Использование объектно-ориентированного программирования - хорошее решение при разработке крупных программных проектов. Объект и класс как основа объектно-ориентированного языка. Понятие объектно-ориентированных языков. Языки и программное окружение.
контрольная работа [60,1 K], добавлен 17.01.2011Основные элементы объектной модели. Сущность и преимущества объектно-ориентированного подхода, понятие объекта и класса. Унифицированный язык моделирования UML. Диаграммы классов и взаимодействия: назначение, построение и примеры использования.
реферат [273,2 K], добавлен 09.06.2009C# как объектно-ориентированный язык программирования. Объектно-ориентированный анализ и проектирование системы на языке UML. Сущность программы "Учёт пациентов в регистратуре поликлиники", ее достоинства и недостатки, пошаговая инструкция пользователя.
курсовая работа [1,5 M], добавлен 17.02.2013Приемы и правила объектно-ориентированного программирования с использованием языка С++. Общие принципы разработки объектно-ориентированных программ. Основные конструкции языка С++. Разработка различных программ для Windows с использованием WIN32 API.
учебное пособие [1,6 M], добавлен 28.12.2013Изучение принципов объектно-ориентированного программирования, в котором основными концепциями являются понятия классов и объектов. Свойства этого вида программирования: инкапсуляция, полиморфизм, наследование. Описание класса. Конструкторы и деструкторы.
презентация [74,8 K], добавлен 14.10.2013