![](/user_photo/2706_HbeT2.jpg)
GrandM-Patterns_in_Java
.pdf400 • Глава 8. Поведенческие шаблоны проектирования
|
break; |
|
case LOW POWER: |
|
case DIAGNOSTIC : |
|
sm . diaqnosticAlert (device) ; |
|
break; |
) |
/ / swi t ch |
/ / |
n o t i f y ( in t , int) |
/ / clas s Securi tyAdapter
ШАБЛОНЫ ПРОЕКТИРОВАНИЯ, СВЯЗАННЫЕ С ШАБЛОНОМ OBSERVER
Adapter. Шаблон Adapter может использоваться для того, чтобы разрешить
объектам, не реализующим необходимый интерфейс, участвовать в шаблоне Observer и получать извещения от своего имени.
Delegation. Шаблон Observer использует шаблон Delegation.
Mediator. Шаблон Mediator иногда используется для координации изменений состояния объекта Observable, инициируемых несколькими объектами.
PubIish-SuЬsсriЬеr. Шаблон PubIish-SuЬsсriЬег (описанный в книге [Grand2001])
это специальная версия шаблона Observer, предназначенная для передачи изве щений удаленным и распределенным объектам.
Этот шаблон бьm описан в работе [GoF95].
СИНОПСИС
Инкапсулирует состояния объекта в виде отдельных объектов, каждый из кото рых расширяет общий суперкласс.
КОНТЕКСТ
Многие объекты должны иметь динамически изменяющийся набор атрибутов, который называется их состоянием. Такие объекты называются объе"тами, имеющими состояние. Как правило, состояние объекта представляет собой один из предварительно заданных наборов значений. Когда имеющий состояние объект узнает о внешнем событии, его состояние может измениться. Поведение объекта, имеющего состояние, в какой-то мере определяется его состоянием.
В качестве примера объекта, имеющего состояние, рассмотрим случай написа ния диалогового окна для редактирования параметров программы. Окно будет иметь• кнопки для внесения выполненных изменений:
•так и в рабочих переменных программы;
•Save, которая сохраняет значения параметров только в файле;
Apply, которая сохраняет значения параметров только в рабочих перемен
•ных программы;
Revert, которая восстанавливает переменные диалогового окна, записывая
вних значения из файла.
Можно спроектировать диалоговое окно так, чтобы оно не отслеживало со стояний. Если диалоговое окно не отслеживает состояния, то оно всегда будет
вести себя одинаково. Кнопка ОК будет доступна независимо от того, редакти Ровались или нет значения переменных. Кнопка Revert будет доступна даже
в том случае, если пользователь только что изменил значения переменных на те, которые хранились в файле. Если нет других условий, проектирование такого
диалогового окна без учета состояний считается удовлетворительным.
В некоторых случаях поведение диалогового окна, не отслеживающего свои со
стояния, может приводить к появлению проблем. Обновление значений рабо чих переменных программы может быть произведено неправильными данными.
Сохранение значений параметров в файле может потребовать слишком много
![](/html/2706/346/html_MxkKzTLKJz.Ja4Q/htmlconvd-LkZgZ6393x1.jpg)
![](/html/2706/346/html_MxkKzTLKJz.Ja4Q/htmlconvd-LkZgZ6394x1.jpg)
State - 403
DirtyState
ItnQtDiш ; QtDiш
Ifil!:Diш ; Еi Diш
Il!iil riill!!Diш; [ilI!!Diш
ItQQthDi ; BothDi +diшЕ !:!] t;iпt 12{Ш!i:!:П} +ill:!l:! Ev!:nt: i!]!•.3 {frQl:!Ш}
+Si!v!:EV!:Qt; int· {fr!!i:!:!]} +[!:ver1Ev ent:int • 4 {frozen}
-сreate() #enterO
#nextState(event:int) :OirtyState
+ tar1( ill!l:! ;B UnQ!],i!ve ;BUnQn,
r!:v!:r1B uttQQ) ; Di Sti!t!: +processEvent(event : int) :DirtyState
|
|
|
|
|
|
|
|
|
|
|
|
|
NotDirtyr |
|
|
t |
|
|
|
|
|||
|
|
|
|
ParamDirtyI |
|
|
|
|
|||
|
|
|
|
|
|
|
|
||||
|
-parent : DirtyState |
|
|
-parent : DirtyState |
|
|
|
|
|||
|
+nextState(event:int) :DirtyState |
|
|
+nextState(event:int) :DirtyState |
|
|
|||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FileDirty |
|
|
|
|
BothDirty |
||
|
|
-parent : DirtyState |
|
|
-parent : DirtyState |
||||||
|
|
+nextState(event:int) :DirtyState |
|
+nextState(event:int) :DirtyState |
Рис. 8.21. Класс DirtyState
он вызывает абстрактный метод nextState. Каждый подкласс класса Dir tyState соответствующим образом замещает метод рrосе s sЕvеnt дляопреде ления следующего состояния. Класс Di rtyState имеет также статический ме
Тод под названием start.
Метод start активизирует процесс, создавая экземпляр КЩОГО подкласса
КЛасса DirtyState и возвращая исходное состояние. Кроме того, метод start создает экземпляр класса DirtyState и инициализирует его переменные
!1otDirty, fi leDi rty, paramDi rty и bothDi rty соответствующими экземп лярами подклассов, которые он создает.
Класс D i rtyState определяет защищенный метод под названием enter. Ме
тод enter объекта DirtyState вызывается в том случае, когда объект стано-
![](/html/2706/346/html_MxkKzTLKJz.Ja4Q/htmlconvd-LkZgZ6395x1.jpg)
State - 405
Опишем роли, исполняемые этими классами.
Context. Экземпляры классов в этой роли реализуют поведение, специфичное для конкретного состояния. Экземпляры класса Context определяют свое те кущее состояние, поддерживая ссылку на экземпляр конкретного подкласса класса State. Подкласс класса State задает состояние.
State. Класс State представляет собой суперкласс для всех классов, исполь зуемых для представления состояния объектов Context. Класс State опреде ляет• следующие методы.
•подклассы класса State замещают этот метод.
Метод start выполняет всю необходимую инициализацию объектов управле ния состоянием и возвращает объект, соответствующий исходному состоянию клиентского объекта. Перед тем как он возвращает объект State, представ
•ляющий исходное состояние- , он вызывает метод enter объекта State. Метод nех tS ta te это абстрактный метод, которому передается пара метр, описывающий случившееся событие, и который возвращает следую щее состояние. Каждый конкретный подкласс класса State замещает ме
•тод nextState для определения нужного следующего состояния.
Метод ех i t объекта S ta te вызывается в том случае, когда состояние, зада ваемое этим объектом, перестает быть текущим. Класс State обеспечивает реализацию по умолчанию этого метода, которая ничего не делает. Обычно
•подклассы класса State замещают этот метод.
Метод proces sEvent является открытым методом, который принимает ар гумент, описывающий случившееся событие, и возвращает новое текущее состояние. Метод processEvent вызывает метод nextState. Если объект,
|
возвращаемый методом nextState, не является текущим объектом State, |
|
то текущим станет новое состояние. В этом случае метод processEvent вы |
• |
зывает метод exi t прежнего текущего состояния, а затем вызывает метод |
enter нового текущего состояния. |
|
Методы operation l , operation2 И Т.д. реализуют операции, которые ве |
|
|
дут себя по-разному ДЛЯ разных состояний. Например, если объект имеет |
|
состояния, связанные с ним и имеющие имена Оп и Off, реализация опера |
|
ции ДЛЯ состояния Оп может предусматривать какие-то действия, а реализа |
|
ция для состояния Off может предполагать отсутствие действий. Проекти |
|
рование этих методов относится к сфере деятельности шаблона Polymor |
|
phism, описанного в книге [Grand99]. |
Класс State определяет константы, которые являются символьными именами Кодов событий, передаваемых методу proce ssEvent.
Если класс State не имеет экземплярных переменных, нет необходимости иметь более одного экземпляра этого класса. Если существует только один
406 • Глава 8. Поведенческие шаблоны проектирования
экземпляр конкретного подкласса класса State, то класс State будет содер жать статическую переменную, которая ссылается на этот экземпляр. Реализа
ции метода proce ssEvent не создают новые экземпляры, а возвращают те эк земпляры, на которые ссылаюнтсяТ.Дэти. переменные.
ConcreteStatel, ConcreteState2 Это конкретные подклассы класса State.
Они должныИсоответствующим образом реализовывать методы operationl , operation2 т.д.
Кроме того, они должны реализовывать метод nextState для определения со
ответствующего следующего состояния для каждого события. Классы могут за
мещать метод enter и/или метод exit с целью реализации соответствующих
действий, выполняемых при входе или выходе из состояния.
РЕАЛИЗАЦИЯ
Ни один класс, за исключением класса State, не должен знать о подклассах
класса State. С этой целью подклассы класса State объявляются как закры тые классы-члены класса ContextState.
СЛЕДСТВдля ИЯ
© Код каждого состояния находится в его собственном классе. Такая ор ганизация позволяет легко добавлять новые состояния без непредвиденных последствий. Поэтому шаблон State хорошо работает как для немноroчис ленных, так и для многочисленных состояний.
©Для клиентов объектов состояний переходы состояний должны быть эле ментарными. Клиент вызывает метод processEvent текущего состояния,
итот возвращает клиенту новое состояние.
©Процедурная реализация поведения, содержащего состояния, обычно пре
дусматривает наличие нескольких-е методов, содержащих команды swi tch
или цепочки команд i f 1 se для распределения кода, зависящего от сои
стояний. Такие цепочки команд иногда могут быть очень длинными
сложными для понимания. Применение шаблона State позволяет отка заться от таких команд switch и цепочек команд if -else. В этом случае
логика является более сцепленной, и классы в результате должны иметЬ
меньшие методы.
©Применение шаблона State предполагает меньше строк кода и меньше ветвей
выполнения. Этот шаблон позволяет упростить тестирование программы прИ
помощи шаблона White вох Testing, описанного в книге [Grand99].
©Объекты состояний, которые представляют непараметрические состОЯНИЯ,
могут использоваться как одиночки, если нет необходимости в созданиИ нового экземпляра класса S tate. В некоторых случаях (подобных примеру,
описанному в разделе «Контекст» ) нет необходимости создавать экземпляР класса State С целью предоставления набору объектов состояний способа
![](/html/2706/346/html_MxkKzTLKJz.Ja4Q/htmlconvd-LkZgZ6399x1.jpg)
408 • Глава 8. Поведенческие шаблоны проектирования
Метод start класса Di rtyState инициализирует конечный автомат. Его аргу ментами ЯWIяются: объект Parameters, который может использоваться конеч
ным автоматом ДЛЯ обновления рабочих переменных программы, и кнопки,
разрешаемые или запрещаемые конечным автоматом. Метод s tart возвращает исходное состояние.
public static DirtyState start (Parameters р,
|
|
Button apply, |
|
|
|
Button |
save , |
|
|
Button |
revert) { |
|
DirtyState d = new Dirtystate () ; |
|
|
|
d . parameters = р; |
|
|
|
d . apply = apply; |
|
|
|
d . save = зауе ; |
|
|
|
d . revert = revert; |
|
|
|
d . notDirty. enter () ; |
|
|
|
return d . notDirty ; |
|
|
|
1 1 s tart ( But ton , But ton , Button ) |
|
|
1 * * |
|
|
|
* |
О'I'вечае'I' на данное соБЫ'I'ие следующим СОС'I'оянием . |
||
* |
@pa ram |
event Код собы'I'ИЯ . |
|
* |
@ return |
Возвращае'I' следующее СОС'I'ояние . |
protected DirtyState nextState (int event) {
/ / Э'I'О'I' незамещенный метод не должен вызыва'I'ЬСЯ никогда . throw new IlleqalAccessError () ;
/ / nextState ( int)
1 * *
* О'I'вечае'I' на данное соБЫ'I'ие, определяя следующее 'I'екущее * состояние и переходя к нему, если оно оказывается
*отличным от текущего .
*/
public final DirtyState processEvent (int event) DirtyState myNextState = nextstate (event) ; if (this ! =myNextState) {
myNextState . enter ( ) ;
} / / if
return myNextState ;
/ / proce ssEven t ( in t )
/* *
* Если данный объект становится текущим состоянием,
![](/html/2706/346/html_MxkKzTLKJz.Ja4Q/htmlconvd-LkZgZ6400x1.jpg)
State _ 409
* вызывается этот метод .
* /
protected void enter ( ) { }
Четыре конкретных подкласса класса Di rtyState реализуются как закрытые классы. Для краткости здесь приводится только один из этих классов.
/ * * |
|
|
* |
Класс |
представляет такое состояние, когда содержимое |
* |
полей |
диалога не совпадает с содержимьш файла |
* или значениями рабочих параметров . |
||
* / |
|
|
private class BothDirty extends DirtyState
/* *
* Отвечает на данное событие .
* |
@ re turn Возвращает следующее состояние . |
||
* / |
|
|
|
switch |
(event) { |
||
|
сазе |
DIRTY EVENT : |
|
|
|
return this i |
|
|
сазе APPLY EVENT : |
||
|
|
if |
(parameters . applyParam(» |
|
|
fileDirty . enter () i |
|
|
|
return fileDirtYi |
|
|
|
/ / |
i f |
сазе SAVE EVENT : |
|||
|
if |
(parameters . saveParam(» |
|
|
|
|
paramDirty. enter ( ) j |
|
|
|
return paramDirtY i |
|
} |
/ / |
i f |
сазв REVERT_EVENT : |
|||
|
if |
(parameters . revertParam (» |
|
|
|
|
paramDirty. enter () i |
|
) |
/ / |
return paramDirtY i |
|
i f |
||
public DirtyState nextState (int event) { |
|||
default : |
|||
|
String шзс] = "unexpected event "+eventj |
||
|
throw new IllegalArgumentException (msg) i |
||
} / / |
switch ( even t ) |
/ / nextState ( i n t )