На этом шаге рассмотрим два типа отношений между объектами - связь и агрегацию.
Сами по себе объекты совершенно не интересны. Поведение системы определяется лишь взаимодействием между объектами. Отношения между двумя объектами основываются на взаимных предположениях о допустимых операциях и ожидаемом поведении. Особый интерес для объектно-ориентированного анализа и проектирования представляют два типа отношений между объектами:
На рис. 1 продемонстрированы несколько разных связей.
Рис.1. Пример связи объектов
Линия, соединяющая две пиктограммы, которые соответствуют объектам, обозначает существование связи между ними и передачу сообщений от одного к другому. Сообщение изображается в виде маленькой стрелочки, указывающей направление передачи, с меткой, представляющей собой его содержание. Например, на рис. 1 показан фрагмент упрощенной схемы управления потоком жидкости в заводской трубе.
Как видим, объект ДАТЧИК имеет связь с объектом ТРУБА. В свою очередь, объект ТРУБА имеет связь с объектом ДИСПЛЕЙ, отражающим его состояние. Передача сообщений может осуществляться только в направлении связи.
Передача сообщений между объектами обычно носит односторонний характер, хотя иногда она бывает двусторонней. В нашем примере объект ДАТЧИК выполняет операции над объектами ТРУБА (чтобы изменить его настройки) и ДИСПЛЕЙ (чтобы изменить текст, который он отображает).
Однако оба эти объекта не воздействуют на объект ДАТЧИК. Это разделение обязанностей является довольно типичным для хорошо структурированных объектно-ориентированных систем. Несмотря на то что сообщение создается клиентом (ДАТЧИК) и направляется серверу (ТРУБА), данные могут передаваться в обоих направлениях связи. Например, объект ДАТЧИК выполняет над объектом ТРУБА операцию ИЗМЕНИТЬ_НАСТРОЙКИ и данные (т.е. изменяемая настройка) передаются от клиента к серверу. Однако, если объект ДАТЧИК выполнит над объектом ТРУБА операцию ЗАКРЫТЬ, то результат (т.е. индикатор закрытого положения трубы) будет передан от сервера к клиенту.
Участвуя в связи, объект может выполнять одну из следующих трех ролей:
На рис. 1 объект ДАТЧИК действует как контроллер, объект ДИСПЛЕЙ — как сервер, а объект ТРУБА — как агент.
Рассмотрим на примере распределение обязанностей в группе взаимодействующих объектов. Во многих промышленных процессах требуется обеспечить пилообразное изменение температуры: поднять ее до заданного значения, выдержать в течение фиксированного периода, а затем понизить до нормального уровня. Разные процессы имеют разные профили температуры. Некоторые объекты (например, зеркало телескопа) следует охлаждать медленно, а другие (например, сталь) — быстро. Эта абстракция температурного профиля имеет четкие характеристики и может быть реализована в виде класса ТЕМПЕРАТУРНЫЙ_ПРОФИЛЬ (рис. 2).
Рис.2. Пример агрегации
На самом деле поведение этой абстракции представляет собой нечто большее, чем зависимость температуры от времени. Например, через 60 мин температура должна достичь уровня 250 градусов, а через 180 мин — 150 градусов. Возникает вопрос: какой должна быть температура на 120-й минуте? Для того чтобы ответить на этот вопрос, необходимо выполнить линейную интерполяцию, так что поведение абстракции должно предусматривать эту операцию.
Рассмотренная абстракция не предусматривает управления нагревателем, обеспечивающего требуемый профиль температуры. Вместо этого осуществляется более глубокое разделение обязанностей, обеспечиваемых путем взаимодействия трех объектов: температурного профиля, нагревателя и контроллера (рис. 2).
На первый взгляд, единственное предназначение описанной абстракции — инкапсулировать функциональную декомпозицию внутри класса, чтобы имитировать объектно-ориентированный стиль. Операция ОПРЕДЕЛИТЬ() опровергает это предположение. Объекты класса ТЕМПЕРАТУРНЫЙ_КОНТРОЛЛЕР имеют достаточно информации, чтобы определить график нагрева, поэтому эту операцию можно считать дополнительным поведением абстракции. В некоторых энергоемких технологиях (например, в металлургии) нагревание представляет собой очень дорогой процесс, поэтому наряду с остыванием автоматического нагревателя необходимо учитывать остаточное тепло от предыдущей плавки. Благодаря операции ОПРЕДЕЛИТЬ(), клиент может поручить объекту ТЕМПЕРАТУРНЫЙ_КОНТРОЛЛЕР определить оптимальный момент для включения нагревателя, чтобы обеспечить требуемый температурный режим.
Рассмотрим два объекта А и В, между которыми существует связь. Для того чтобы объект А мог послать сообщение объекту В, необходимо, чтобы объект А имел доступ к нему. В ходе анализа задачи видимость объектов, чаще всего, можно игнорировать, но при разработке конкретной реализации системы необходимо обеспечить доступ связанных объектов друг к другу.
Когда один объект посылает сообщение другому по линии связи, говорят, что эти объекты синхронизируются. В последовательном приложении синхронизация объектов представляет собой обычный вызов метода. Однако в многопоточных системах объекты нуждаются в более изощренных схемах передачи сообщений, чтобы разрешить проблемы взаимного исключения, типичные для параллельных систем.
Как уже отмечалось, активные объекты имеют свои собственные потоки управления, поэтому другие активные объекты на их семантику, как правило, не влияют. Однако если активный объект имеет связь с пассивным, возможны три варианта синхронизации:
В то время как связи обозначают равноправные или "клиент-серверные" отношения между объектами, агрегация описывает иерархию "целое/часть" и позволяет переходить от целого (агрегата) к его компонентам. В этом смысле агрегация является частным случаем ассоциации. Например, как показано на рис. 2, объект ТЕМПЕРАТУРНЫЙ_КОНТРОЛЛЕР представляет собой нечто целое, а объект НАГРЕВАТЕЛЬ является его частью.
Подразумевается, что объект, представляющий собой часть другого объекта, имеет связь со своим агрегатом. Используя эту связь, агрегат может посылать сообщения своим частям. Например, объект ТЕМПЕРАТУРНЫЙ_КОНТРОЛЛЕР может посылать сообщения объекту НАГРЕВАТЕЛЬ. Для того чтобы перейти от объекта НАГРЕВАТЕЛЬ к объекту, который его содержит (контейнеру), необходимо, чтобы информация о контейнере (container) была частью состояния объекта НАГРЕВАТЕЛЬ.
Агрегация не всегда означает, что один объект буквально включает в себя другой объект. Например, самолет физически состоит из крыльев, двигателей, шасси и прочих частей. С другой стороны, отношение акционера с его акциями — это агрегация, не означающая физическое включение. Акционер единолично владеет своими акциями, но не состоит из них. Эти отношения целое/часть носят абстрактный, а не физический характер.
Между связями или агрегацией существует явный компромисс. Агрегация иногда предпочтительнее, поскольку инкапсулирует части целого, скрывая их от внешних клиентов. В других случаях, наоборот, предпочтительнее связи, поскольку они позволяют ослабить взаимоотношения между объектами. Разумное проектное решение требует тщательного анализа этих двух факторов.
На следующем шаге рассмотрим понятие класса.