Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

GrandM-Patterns_in_Java

.pdf
Скачиваний:
98
Добавлен:
14.03.2016
Размер:
8.88 Mб
Скачать

358 Глава 8.

Поведенческие шаблоны проектирования

/ / Объединяет

общие результаты .

int [ )

myResult

= new int [commonCount) ;

commonCount =

О ;

 

for (int 1=0 , r=0 ;

 

1<leftResu1t . 1ength && r<rightResu1t . 1ength ; ) {

if

(leftResu1t[1) < rightResu1t [r) {

 

1++;

 

> rightResu1t [r)

e1se if (leftResu1t[1)

 

r++;

 

 

else

 

 

 

myResu1t [commonCount)

= 1eftResult[1) ;

 

commonCount++ ;

 

1++ ;

 

 

r++ ;

 

 

}

/ / i f

 

 

/ /

for

 

 

return myResu1t;

// contains ( String)

//class AndCombination

c1ass OrCombination extends Combination { private Combination 1eftChi1d, rightChi1d;

/ * *

 

 

*

Constructor

*

@pa ram

left Левый потомок этого объекта .

*

@pa ram

right Правый потомок этого объекта .

* /

OrCombination=(Combination 1eft . Combination right) { 1eftChi1d =1eft;

rightChi1d right;

// con s tructor ( Combination, Combinat ion )

int [ )

contains (String з) {

 

int [ )

leftResu1t =

1eftChi1d . contains (s) ;

int [ )

rightResu1t

=

rightChi1d . contains (s) ;

if

(leftResu1t ==

nul1)

 

 

return rightResu1t;

 

if

(rightResu1t ==

nu1l)

О)

 

return leftResult;

if

(leftResult. length ==

Little Language 359

return leftResult;

if (rightResult. length == О) return rightResult;

// Создает массив объединенных результатов . nt [] myResult

= new int [leftResult . length + rightResult . length] ; System . arraycopy (leftResult , О , myResult, О ,

leftResult . length) ;

System. arraycopy (rightResult, О , myResult, leftResult . length ,

rightResult . length) ; return myResult;

//contains ( S t ring)

// class OrCombination

ШАБЛОНЫ ПРОЕКТИРОВАНИЯ, СВЯЗАННЫЕ

С ШАБЛОНОМ LIПLЕ LANGUAGE

Composite. дерево синтаксического разбора организовано при помощи шаблона

Composite.

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

СИНОПСИС
Шаблон Mediator

,

Этот шаблон ранее был описан в работе [GoF95].

использует один объект ШIЯ согласования изменения состояний

других объектов. Вместо распределения логики по разным классам он помеща­

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

КОНТЕКСТ

Шаблон Mediator решает проблему, которая часто встречается при использова­ нии диалоговых окон. Предположим, что для задания информации ДЛЯ резер­ вирования банкетного зала в отеле необходимо реализовать диалоговое окно

(рис. 8.10).

N umber of People (25-1 600):1 -1 _---J

Date (MM/DDIY'Y): 1

 

 

S tart Тiтe (Н Н:М М)"': -;1==== :::;--"

 

 

End Time (Н Н:ММ): LI ..._...J

,

::(1РП;;

 

/

I ! :.:

I Cancel

I

Рис. 8.10. Диалоговое окно для заказа банкетного зала

-

Mediotor _ 361

Назначение этого диалогового окна состоит в том, чтобы предоставить необхо­ димую информацию ДЛЯ резервирования банкетного зала в отеле. Условия диа­

логового окна соответствуют зависимостям между объектами диалога.

При первом появлении диалогового окна доступны только поля Number of People ( Количество человек) и кнопка отмены (Cancel). До тех пор пока в это поле не будет введено число в диапазоне от 25 до 1600, остальная часть диалога недоступна. После ввода количества человек становятся доступны­ ми поля Date (Дата) , Start Time (Время начала) и End Time (Время кониа), но они позволяют вводить только те значения времени, когда зал нужных размеров не занят. Доступны также кнопки-переключатели. Последующие изменения, вводимые в поле Number of People, делают доступными другие

поля и кнопки -переключатели.

Время начала должно быть раньше времени кониа.

Когда пользователь ввел данные в поля времени и даты и выбрал кнопку­ переключатель, появляется список блюд. Заказанные дата, время и вид об­ служивания определяют блюда, входящие в список. Некоторые блюда явля­ ются сезонными, и отель предлагает их только в течение некоторого перио­

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

мом только для утренних банкетов. Некоторые блюда не годятся для

шведских столов, а должны подаваться официантами.

Если выбрано хотя бы одноокблюдо. и текстовые поля содержат правильные данные, доступна кнопка

Если каждый объект диалогового окна отвечает за те зависимости , с которыми

он связан, в результате получится сильно связанный набор объектов, имеющих слабое сuепление (рис. 8.1 1).

Дли я упрощения этой диаграммы на ней не указаны имена ассоuиаuий, ролей

индикаторы множественности. Целью диаграммы является отображение всех

связей. Очевидно, что каждый объект участвует по крайней мере в двух зависи­

мостях, а для некоторых объектов число зависимостей доходит до пяти. Боль­

шая часть времени, затрачиваемого на реализаuию диалогового окна, пойдет на кодирование 15 связей зависимости.

Логика управления зависимостью распределена по восьми объектам, поэтому

рнадеализаuия диалогового окна сопряжена с немалыми трудностями. При работе диалоговым окном программист будет ВИдеть только небольшую часть Управления зависимостями. Ему будет сложно понять детали управления зави­ Симостями в uелом , и он не будет тратить на это время. Если программисты,

Поддерживая код, не имеют полного представления обо всех деталях, сопрово­ Ждение отнимает много времени, и часто его качество невысоко.

ОчевИдНО, что необходи ма реорганизаuия этих объектов, позволяющая сделать

вКОЛичество связей минимальным и объединить все управление зависимостями

одном сuепленном объекте. Такое усовершенствование экономит время про­ Граммиста и позволяет получить более надежный код. И менно для этой цели

362 Глава 8. Поведенческие шаблоны проектирования

r

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

l!eQl!l!:CountFi!:ld;JT!: Fi!: d

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ditiFi!:ld;JT!:xt

 

 

 

 

 

 

 

 

 

 

ld

 

 

 

 

 

I stаrtFi!:ld:ЛехtFiеld

:

 

 

 

:

 

 

 

 

1

 

 

 

 

 

 

!:ndFi!:

;JT!:1rtEi!:

 

r tibleSeryjc!:Butt:Qn;;JRidiQButt:on1

 

 

I

 

 

 

 

 

 

 

 

I

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

buffetButt:on:JRadiQButton

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

foodLi t:JLi t

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1

Q B!.IttQ[!;JB .IttQDt---

 

 

 

 

 

 

 

Рис. 8.11. Децентрализованное управление зависимостями

предназначен шаблон Mediator. При использовании этого шаблона объект не должен самостоятельно управлять своими связями с другими объектами, вме­ сто этого применяется другой объект, который берет на себя все управление за­ висимостями. При такой организаuии каждый объект имеет только одну связь зависимости.

На рис. 8.12 представлены объекты диалога, организованные при помоши до­

полнительного объекта, предназначенного ДЛЯ uентрализованного управления зависимостями .

Помимо упрошения реализаuии и поддержки кода, представленный на рис. 8.12

проект более понятен, чем показанный на рис. 8.1 1 .

I

stаrtFiеld:ЛеxtFiеld

I реорlеСоuпtFiеld:ЛеxtFiеld I

еп

е

ld:11ЛехtFiе

11------

.1 1.

 

 

 

dFi

ld

I

I dаtеFiеld:ЛеxtFiеld

:BanquetDialogMediator

 

1

__---1

l

IL...-

--I,

I tableServiceButton:JRadioButt:on 1--

 

__

 

 

 

 

 

 

tt

buffetButt:on:JRadioBu

foodlist:Jlist

I

r

okButt:on:JButt:on

I

Рис. 8.12. Централизованное управление зависимостями

Mediator - 363

МОТИВЫ

©Сушествует набор связанных объектов, и почти все объекты участвуют во

множестве отношений зависимостей.

©При определении подклассов предполагается участие отдельных объектов

в отношениях зависимости.

©Классы трудно использовать многократно, так как их функция усложнена отношениями зависимостей.

РЕШЕНИЕ

На диаграмме взаимодействия, представленной на рис. 8.1 3, показан обший

случай участия классов и интерфейсов в шаблоне Mediator.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

CoLLeaguel

 

 

 

 

 

 

 

 

 

 

 

CoLLeague2

 

Е---

 

 

 

addEventListenerl(:EventListenerl)

 

 

addEventListen

ег2(:ЕуеntListeпег2)

 

1

. . .

 

 

 

 

 

 

 

 

 

 

 

 

 

addEventListen

егЗ(:ЕvепtListепегЗ)

1

 

 

 

 

 

 

 

.....Извещает

 

 

 

 

 

 

 

. . .

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

.....звещаеИ т

 

 

 

 

 

 

 

 

06

изменении

 

 

 

 

 

.....звещИает

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

06

изменении

 

 

06 изменении

 

 

 

 

 

 

lf

состояния

 

 

 

 

 

 

 

 

состояния

 

lf

состояния

 

 

 

 

 

 

 

 

 

 

 

 

«interface»

 

 

 

 

 

 

 

«interface»

 

 

 

 

 

 

«interface»

 

I

 

 

I

 

 

I

 

 

 

 

I

I

 

 

 

 

EventListenerl

 

 

 

EventListener2

 

 

 

 

EventListener3

 

 

 

 

 

 

t::I .

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

I

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

I

 

 

 

 

 

 

 

 

- - - - - - - - - - - - - - - - - - - - - - - - ,

 

I ( - - - - - - - - - - - - - - - - - - - - - - - -

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

I

 

I I

 

 

 

 

 

 

 

 

 

 

 

 

 

....П

 

т из

менения

 

 

 

 

 

 

Mediator

 

 

 

 

 

 

 

Передает изменения .....

 

 

 

 

 

 

registerCoLLeaguel(ColLeaguel)

 

 

 

 

 

ередае

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1

 

registerCoLLeague2(CoLLeague2)

 

1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

. . .

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

handLeEvent1( )

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

handLeEvent2( )

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

. ..

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Рис.

8.13. Классы шаблона Mediator

 

 

 

 

 

 

Опишем роли, исполняемые этими классами и интерфейсами.

 

 

 

COlleag

 

 

 

 

 

 

и т.д.

Экземпляры классов в этой роли имеют зависимо­

 

 

uel, Colleague2

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Сти, связанные с состояниями:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

один тип зависимости требует от объекта получить одобрение со стороны

других объектов на выполнение специальных изменений состояния;

другой ЧТОтип зависимости требует от объекта оповешения иных объектов

отом, он выполнил изменение состояния.

364 Глава 8. Поведенческие шаблоны проектирования

Оба типа зависимостей обрабатываются похожим образом. Экземпляры клас­

сов Col league l , Col league2 и т.д. связаны с объектом Mediator. Если ИМ

нужно оповестить другие объекты об изменении состояния, они вызывают ме­

тод объекта Mediator. Метод иобъекта Mediator делает все остальное.

EventListenerl, EventListener2 Т.д. Интерфейсы в этой роли дают возмож­ ность классам Colleague l , Colleague2 и т.д. достичь высокой степени мно­ гократного использования, поскольку позволяют классам не знать, что они ра­

ботают с объектом Mediator. Каждый из этих интерфейсов определяет методы,

связанные с событием определенного вида. Чтобы обеспечить извещения о со­ стоянии, объекты Colleague вызывают соответствующий метод нужного ин­ терфейса, ничего не зная о классе объекта Mediator, реализующем этот метод.

Mediator. Экземпляры классов в роли Mediator имеют логику для обработки

извещений о состоянии, полученных от объектов Col league l , Colleague2 И т.д. Классы Mediator реализуют один или несколько интерфейсов EventLis­ tener. Объекты Colleague l , Colleague2 вызывают методы, объявленные ИН­ терфейсами EventListener с целью проинформировать объект Mediator об из­ менении состояния. Затем объект Mediator выполняет соответствующую логику:

для извещений о предполагаемых измененияхдлясостояния логика обычно содер­

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

Классы Medi tor имеют методы, которые могут быть вызваны для их связи с объектами Col leaguel , Colleague2 и Т.д. Эти методы указаны на диаграмме как registerColleague l , registerColleague2 и Т.д. Они передаются соот­ ветствующему объекту Col league и обычно вызывают один или несколькоО его методов add . . . Listener с целью проинформировать объект Col league том, что он должен известить объект Mediator об изменении состояния.

Средства, предоставляемые объектами Col leaguel, Colleague2 и т.д. и по­

зволяющие другим объектам выразить свою заинтересованность в изменении

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

РЕАЛИЗАЦИЯ

Во многих случаях один объект отвечает за создание всех объектов Coll eague

и соответствующего объекта Mediator. Такой объект обычно выполняет роль

контейнера для создаваемых им объектов. Если существует единственный объ­

ект, отвечающий за создание всех объектов Co lleague и их объекта Mediator.

то класс Mediator, как правило, объявляется как закрытый член этого класса.

Ограничивая видимость класса Mediator, повышают надежность программЫ.

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

свою собственную внутреннюю модель состояния объектов Col league ИЛИ он

предпочтет считывать состояние каждого объекта всякий раз, когда потребуетсЯ

информация о состоянии объекта.

Mediator 8 365

Если применяется первый подход, то объект Mediator начинает с того, что оп­ ределяет первоначальное состояние всех объектов Col league, за которые отве­

чает. Он будет иметь переменную экземпляра для каждого объекта Colleague. Объект Medi ator задает начальное значение каждой из этих переменных эк­ земпляра, которое, как он считает, должно быть начальным состоянием соот­ ветствующего объекта Colleague. Когда объект Colleague оповещает объект

Mediator об изменении своего состояния, Med iator изменяет значения своих

переменных экземпляра, приводя их в соответствие с новым состоянием. По­

сле обновления своих переменных экземпляра объект Mediator использует

значения этих переменных для принятия нужного решения.

Если применяется второй подход, то объект Mediator не пытается моделировать

состояние объектов Col league при помощи своих перемен ных экземпляра.

Вместо этогоа, когда объект Col league оповещает его об изменении состояния,

объект Medi tor принимает нужные решения, считывая состояние каждого

объекта Colleague, на основании которого он должен принимать эти решения.

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

Недостаток первого механизма состоит в том, что объект Medi ator может иметь неверную информацию о состоянии объекта Coll eague . Чтобы точно моделировать состояние объектов Colleague, объект Mediator может испы­ тывать потребность в дополнительном коде для имитации логики объектов

Col league. Если позднее программист из группы сопровождения изменит

один из классов Col league, такое изменение может разрушить класс Mediator.

Преимущество второго подхода заключается в его простоте. Объект Mediator

никогда не будет иметь неправильную информацию о состоянии объекта

Col league. Поэтому реализация и поддержка объекта Mediator не вызывает затруднений. Однако если чтение полного состояния объектов Colleague тре­

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

при котором объект Mediator моделирует состояние объектов Co lleague.

СЛЕДСТВИЯ

©Почти все сложности, связанные с управлением зависимостями, переходят от разных объектов к объекту Mediator. При этом упрощается реализация и поддержка разных объектов.

©Помещение всей логики зависимостей классов в один класс-посредник помо­ гает программистам, осуществляющим сопровождение, находигь зависимости.

©Использование объекта Mediator обычно приводит к меньшему количествуДЛЯ ветвей выполнения кода. Это значит, что требуется меньше усилий пол­

ного тестирования, поскольку должно проверяться меньше случаев. Влияние меньшего количества ветвей выполнения кода на тестирование подробно рассматривается при описании шаблона White Вох Testing в книге [Grand99].

;66 Глава 8. Поведенческие шаблоны лроектирования

Использование объекта Mediator обычно означает отсyrствие необходимо­

сти создания подклассов классов Col league только для реализации управ­

ления их зависимостями.

Классы Col league являются в большей степени многократно используе­

мыми, так как на их основную функциональность не влияет код, управляю­

ший зависимостями. Он обычно имеет тенденцию зависеть от приложения.

Размещение всей логики зависимостей набора связанных объектов в одном

месте упрощает понимание этой логики зависимостей. Если класс Mediator получается слишком большим, то разбиение его на мелкие части может

привестидля к тому, что он станет менее понятным.

Код управления зависимостями обычно зависит от приложения, поэтому классы Mediator, как правило, не являются многократно используемыми.

lРИМЕНЕНИЕ В JAVAAPI

Iримеры шаблона Mediator, которые можно найти в Java API, немного отлича­ )тся от тех посредников, которые, скорее всего, придется кодировать. Причи­ а в том, что код классов Mediator обычно зависит от приложения, а классы ava АР! от приложения не зависят.

jUI на основе языка Java может быть создан, главным образом, из объектов, оторые ЯRЛЯются экземплярами подклассов класса j ava . awt . swing . JComponent. )бъекты JComponent используют экземпляр подкласса класса j ava . awt . swing . ocusManager В качестве посредника. Если объекты JComponent связаны объектом FocusManager (а они обычно связаны), то они вызывают его метод roces sKeyEvent при получении KeyEvent. Задача объекта FocusManager со­

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

пределенный объект JComponent, что он и делает.

:пособ использования объектами JComponent объекта FocusManager отлича­

гся от описанного в шаблоне Mediator ввиду следующих двух причин:

1. Объекты JComponent передают ключевые события от нажатия клавиШ

только объектам FocusManager . Большая часть классов Mediator, ко­

торые придется писать, должны будyr управлять событиями несколькИХ

видов.

2. Объекты JComponent не обрашаются к объектам FocusManager через

интерфейс. Они прямо ссылаются на класс FocusManager. Если объек­

ты JComponent ССЬUIаются на объекты FocusManager через интерфейс,

структура является более гибкой. Видимо, проектировщики Java АР!

посчитали здесь такую гибкость ненужной, поскольку взаимодействие

между объектами JComponent и объектами FocusManager осуществлЯ­

ется на низком уровне.

Класс BanquetMediator может гарантировать, что только те объекты GUI, которые, как ожидается, должны отправлять события объекту BanquetMe­ diator, действительно могут это сделать. При этом повышается надежность класса BanquetMediator. С этой целью объекты BanquetMediator дела­ ют свои методы управления событиями доступными только ДЛЯ зарегист­ рированных объектов GUI. Все методы управления событиями класса BanquetMedia tor объявляются как закрытые. Объекты-адаптеры являют­ ся экземплярами закрытого или анонимного внутреннего класса, который может вызывать внутренние методы класса BanquetMedi ator. Поскольку классы адаптера являются закрытыми или анонимными, их экземпляры могут создаваться только классом BanquetMediator. Экземпляры классов
ПРИМЕР КОДд

Mediator - 367

Пример кода ДЛЯ шаблона Mediator - это код ДЛЯ объекта-посредника, кото­ рый используется в диалоговом окне, рассматриваемом- в разделе «Контекст» . В этом примере отражена суть шаблона Mediator возложить ответственность за всю сложность управления событиями на классы Mediator.

Класс-посредник реализуется как закрытый внутренни й класс класса диалога под названием BanquetMediator:

private class Banquetмediator private JВutton okButton ; private JТextComponent dateField;

private JТextComponent startField;

Как бьulО показано выше, класс BanquetMediator имеет закрытые переменные экземпляра, используемые им для ссылки на объекты GUI, которые диалоговое

окно объявляет вместе с ним. Класс BanquetMediator не реализует каких-либо интерфейсов EventListener, позволяющих ему получать события от зарегист­ рированных объектов GUI. Вместо этого ДЛЯ получения таких событий он ис­ пользует объекты адаптера. Ниже приводятся две основные причины, почему классBanquetMediator использует классы-адаптеры ДЛЯ получения событий.

адаптера предоставляются только зарегистрированным объектам GUI.

Использование объектов-адаптеров ДЛЯ обработки событий от разных объек­

тов GUI освобождает класс BanquetMediator от необходимости определять,

от какого именно объекта GUI поступило событие. Класс BanquetMediator объявляет закрытые или анонимные классы-адаптеры и использует их для получения событий только от объектов, играющих определенную роль. Объявляя дополнительные классы, класс Banque tMediator освобождает свои классы-адаптеры от необходимости выбора характера поведения, за­ висящего от источника события.

Примеры таких классов-адаптеров приводятся ниже

б

б

ытий,

классы-адаптеры используются для обра отки со

 

в листинге. Анонимные специфичных для каждо-

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]