На этом шаге рассмотрим базовые понятия класса в нотации UML.
Классы – наиболее важные строительные блоки любой объектно-ориентированной системы. Класс – это описание множества объектов с одинаковыми атрибутами, операциями, связями и семантикой.
Класс реализует один или несколько интерфейсов. Классы используются для фиксирования словаря разрабатываемой системы. Эти классы могут включать абстракции, являющиеся частью проблемной области, а также другие классы, составляющие реализацию. Можно применять классы для представления программных сущностей, аппаратных сущностей и даже тех сущностей, которые являются полностью концептуальными.
Хорошо структурированные классы имеют четко очерченные границы и формируют сбалансированное распределение обязанностей в системе.
Моделирование системы включает в себя идентификацию сущностей, которые важны для вашего конкретного представления. Эти сущности формируют словарь системы, которую вы моделируете.
Например, при построении дома вам как будущему владельцу небезразлично, какими будут стены, двери, окна, кабинеты, системы освещения и пр. Каждая из этих сущностей отличается от другой и обладает набором определенных свойств. Стены имеют некую высоту, ширину и являются сплошными. Двери характеризуются теми же признаками, но их к тому же отличает специфическое поведение: они открываются в одну сторону. Окна отчасти схожи с дверьми, поскольку тоже образуют проем в стене, но их функциональность неодинакова. Обычно (хотя и не всегда) окна предназначены для того, чтобы вы смотрели через стекло на улицу.
Отдельные стены, двери и окна редко существуют сами по себе, поэтому вы должны также рассмотреть, как конкретные экземпляры этих сущностей сочетаются друг с другом. Сущности, которые вы идентифицируете, и связи между ними будут зависеть от того, как вы собираетесь использовать различные комнаты вашего дома, как планируете организовать движение из одной комнаты в другую, а также от общего дизайнерского решения.
Рабочие, которые строят дом, сосредоточены на других вещах. Например, водопроводчик, который помогает разрабатывать поэтажный план, заинтересован в расположении водостоков, сифонов и вентиляционных отверстий. Вы как домовладелец не обязательно принимаете в расчет такие вещи (за исключением тех случаев, когда оказываются затронуты важные для вас сущности, – например, когда водосток может быть устроен в полу или вентиляция выходит через крышу).
В UML все эти сущности моделируются классами. Класс – это абстракция сущности, являющейся частью вашего словаря. Класс – не индивидуальный объект, а представление целого множества объектов. Например, вы можете концептуально представлять "стену" как класс объектов с рядом общих свойств (высота, длина, толщина; является ли стена несущей и т.д.). Но можно иметь в виду и вполне конкретную стену, например находящуюся в юго-западном углу вашей гостиной.
Многие языки программирования непосредственно поддерживают концепцию класса. Это очень удобно, потому что создаваемые вами абстракции часто могут быть напрямую выражены на языке программирования, даже если речь идет об абстракциях непрограммных сущностей, таких как заказчик, торговля или переговоры.
UML предлагает графическое представление класса (рис. 1).
Рис.1. Графическое представление класса в UML
Эта нотация позволяет визуализировать абстракцию вне какого-либо определенного языка программирования, притом подчеркнув ее наиболее важные части: имя, атрибуты и операции.
Каждый класс должен обладать именем, отличающим его от других классов. Имя (name) – это текстовая строка. Отдельное имя, взятое само по себе, называется простым, а имя класса с префиксом – именем пакета, в котором расположен этот класс, – квалифицированным. Класс можно изобразить только с указанием его имени, как показано на рис. 2.
Рис.2. Представление простых и квалифицированных имен классов в UML
Имя класса может состоять из любого числа букв, цифр и знаков препинания (за исключением таких символов, как двоеточие или точка, которая применяется для отделения имени класса от имени содержащего его пакета) и записываться в несколько строк. На практике обычно используются краткие имена классов – существительные, взятые из словаря моделируемой системы. Каждое слово в имени класса традиционно пишут с заглавной буквы, например Customer (Покупатель) или TemperatureSensor (ДатчикТемпературы).
Атрибут (attribute) – это именованное свойство класса, описывающее диапазон значений, которые может принимать экземпляр атрибута. Класс может иметь любое число атрибутов или не иметь ни одного. Атрибут представляет некоторое свойство моделируемой сущности, которым обладают все объекты данного класса: например, у каждой стены есть высота, ширина и толщина. Аналогичным образом вы можете описать своих заказчиков: у каждого из них есть имя, адрес, номер телефона, дата рождения. Таким образом, атрибут – это абстракция вида данных или состояния, которое может принимать объект того или иного класса. В каждый определенный момент объект класса характеризуется конкретными значениями каждого из атрибутов. В графическом представлении атрибуты перечислены в разделе, приведенном непосредственно под именем класса, причем с обязательным указанием их имен (рис. 3).
Рис.3. Представление атрибутов класса в UML
Имя атрибута, как и имя класса, может представлять собой текст. На практике для именования атрибута используются одно или несколько коротких существительных, выражающих некое свойство класса, к которому относится атрибут. Обычно каждое слово в имени атрибута пишется с заглавной буквы, за исключением первого, например name (имя) или birthDate (датаРождения).
Можно уточнить спецификацию атрибута, указав его класс и начальное значение по умолчанию, как показано на рис. 4.
Рис.4. Представление уточненных атрибутов класса в UML
Операция (operation) – это реализация услуги, которая может быть запрошена у любого объекта данного класса, чтобы вызвать определенное его поведение. Другими словами, операция – это абстракция чего-либо, что вы можете сделать с конкретным объектом и вообще со всеми объектами данного класса. Класс может иметь любое число операций либо не иметь ни одной. Например, в оконной библиотеке, все объекты класса Rectangle (Прямоугольник) можно перемещать, изменять их размер или запрашивать их свойства. Часто (хотя и не всегда) вызов операции объекта изменяет его данные или состояние. Графически операции представлены в разделе списка, приведенного под атрибутами класса. Допускается указание только имен операций (рис. 5).
Рис.5. Представление операций класса в UML
Имя операции, как и имя класса, может представлять собой текст. На практике для именования операции используются короткие глагольные конструкции, описывающие некое поведение класса, которому принадлежит операция. Обычно каждое слово в имени операции пишется с заглавной буквы, за исключением первого, например move (переместить) или isEmpty (пуст).
Можно специфицировать операцию, устанавливая ее сигнатуру, включающую имя, тип и значение по умолчанию всех параметров, а применительно к функциям – тип возвращаемого значения (рис. 6).
Рис.6. Представление уточненных операций класса в UML
Изображая класс, вы не обязаны показывать сразу все его атрибуты и операции. Фактически в большинстве случаев вы не в состоянии этого сделать (таких элементов может быть слишком много, чтобы все они поместились на одном рисунке), да и не должны (для конкретного представления, как правило, существенна только часть атрибутов и операций класса). В силу этих причин допускается упрощенное представление класса, то есть для графического представления выбираются только некоторые из его атрибутов и операций – или же они вообще не изображаются. Если помимо указанных существуют другие атрибуты и операции, вы даете это понять, завершая каждый список многоточием. Можно и вовсе не обозначать соответствующий раздел на пиктограмме класса – тогда не будет видно, имеет ли класс атрибуты или операции, сколько их и т.п.
Чтобы лучше организовать длинные списки атрибутов и операций, желательно снабдить префиксом (именем стереотипа) каждую категорию в них, как показано на рис. 7.
Рис.7. Стереотипы операций класса в UML
Обязанность (responsibility) – это соглашение или обязательства класса. Когда вы создаете класс, выдвигается предположение, что все его объекты характеризуются одинаковым состоянием и одинаковым поведением. На более абстрактном уровне соответствующие атрибуты и операции являются просто средствами, благодаря которым класс выполняет свои обязанности. Класс Wall (Стена) отвечает за информацию о высоте, ширине, толщине; класс FraudAgent (АгентПоПредотвращениюМошенничеств), который можно встретить в приложении, обрабатывающем кредитные карты, – за обработку запросов и определение их обоснованности, подозрительности или незаконности; класс TemperatureSensor (ДатчикТемпературы) – за измерение температуры и подачу сигнала тревоги в случае превышения допустимого предела.
Когда вы моделируете классы, для начала очень неплохо сформулировать обязанности сущностей из вашего словаря. Методика применения CRC-карт и анализа на основе вариантов использования может здесь оказаться особенно полезной. У класса может быть сколько угодно обязанностей, хотя на практике каждый хорошо структурированный класс имеет как минимум одну обязанность и как максимум – небольшой их набор. По мере детализации ваших моделей вы будете отображать эти обязанности на множество атрибутов и операций, которые наилучшим образом им соответствуют.
Графически обязанности могут быть представлены в специально отведенном для них разделе, в нижней части пиктограммы класса (рис. 8).
Рис.8. Обязанности класса в UML
Обязанности выражаются текстом в свободном формате. Отдельная обязанность представлена фразой, предложением или (как максимум) коротким абзацем.
Атрибуты, операции и обязанности – это наиболее часто используемые средства, которые понадобятся вам при создании абстракций. Фактически для большинства моделей, которые вы строите, базовая форма этих трех компонентов – все, что нужно для выражения самых важных частей семантики ваших классов. Однако иногда у вас будет возникать потребность в визуализации или спецификации некоторых других характеристик (таких как видимость отдельных атрибутов операций); свойств операций, зависимых от языка (таких как полиморфизм или константность); либо даже исключений, которые объект класса может генерировать или обрабатывать. Эти и многие другие средства могут быть выражены средствами UML.
Приступив к построению моделей, вы очень скоро обнаруживаете, что почти каждая абстракция, которую вы создаете, представляет собой класс определенного вида. Иногда реализацию класса необходимо отделять от его спецификации. В UML это может быть выражено посредством интерфейсов.
При проектировании реализации класса вам потребуется моделировать ее внутреннюю структуру как набор взаимосвязанных частей. Вы можете разделить внутреннюю структуру класса верхнего уровня на несколько слоев.
Когда вы перейдете к проектированию более сложных моделей, то обнаружите, что вам все чаще приходится иметь дело с некоторыми новыми сущностями: классами, представляющими параллельные процессы и потоки, либо классификаторами, описывающими физические сущности, такие как апплеты, компоненты, файлы, Web-страницы и аппаратное обеспечение. Поскольку сущности подобного рода встречаются очень часто и представляют собой важные архитектурные абстракции, для их моделирования в UML предусмотрены активные классы (описывающие процессы и потоки), классификаторы, в частности артефакты (описывающие физические компоненты программного обеспечения), и узлы (описывающие аппаратные устройства).
Наконец, классы редко существуют сами по себе. Когда вы строите модели, то, как правило, фокусируете внимание на группах классов, взаимодействующих друг с другом. В UML такие сообщества классов формируют кооперации и обычно визуализируются в диаграммах классов.
На следующем шаге рассмотрим типичные приемы моделирования классов в UML.