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

GrandM-Patterns_in_Java

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

400 Глава 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 будет доступна даже

в том случае, если пользователь только что изменил значения переменных на те, которые хранились в файле. Если нет других условий, проектирование такого

диалогового окна без учета состояний считается удовлетворительным.

В некоторых случаях поведение диалогового окна, не отслеживающего свои со­

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

Сохранение значений параметров в файле может потребовать слишком много

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

избежать выполнения ненужных операций сохранения в файл или задания He

верных значений рабочих параметров программы, нужно сделать диалОГОВое окно с поддержкой своих состояний. Тогда операции будут разрешены только

в том случае, когда файл или рабочие переменные изменяются на данные, OТ

личные от уже хранящихся. На рис. 8.20 показана диаграмма состояний, дe

монстрирующая четыре состояния, которые необходимы для задания такого

поведения.

..

Кнопка

ApplyjapplyParam()

H"" ,"

Не иэменен

 

 

 

 

ЕпtегjЗапрещены кнопки диалогового окна

 

 

Save, Apply и Revert

 

Кнопка

 

Неиэмененный

 

SavejsaveParam()

Файn иэменен

 

 

 

 

ЕпtегjРаэреwены кнопки диалогового окна

Save и Revert; Запрещена кнопка Apply

Кнопка

Неиэмененный ApplyjapplyParam()

Ни файn, ни параметр не иэменен",

 

ЕпtегjРаэреwены кнопки диалогового окна

 

Save, Apply и Revert

Кнопка

Кнопка

RevertjrevertParam()

RevertjrevertParam()

 

Кнопка

 

Неиэмененный

ApplyjapplyParam()

Параметр иэменен

 

 

 

 

ЕпtегjРаэреwена кнопка диалогового окна Apply;

 

Запрещены кнопки Save и

Revert

Рис. 8.20. Диаграмма состояний диалогового окна, содержащего параметры

Чтобы реализовать диаграмму состояний, представленную на рис. 8.20, можнО

спроектировать классы, показанные на рис. 8.21.

На диаграмме классов изображены четыре класса, которые соответствуют четыреМ

состояниям, представленным на диаграмме состояний, и их общий суперкласс. Суперкласс DirtyState имеет открытый метод под названием processEvent. Метод processEvent ПРинимает в качестве аргумента идентификатор событИЯ

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 вызывается в том случае, когда объект стано-

Метод enter объекта State вызывается в том случае, когда состояние, представленное этим объектом, становится текущим. Класс State обеспе­ чивает по умолчанию бездействующую реализацию этого метода. Обычно

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 С целью предоставления набору объектов состояний способа

State 407

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

земпляром класса S tate.

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

ПРИМЕР КОДА

Приведем код, который реализует диаграмму классов, описанную в разделе

(,Контекст» (рис. 8.21).

class DirtyState

/ / Символьные константы

для

событий .

puыic static final int DIRTY EVENT puыic static final int APPLY EVENT puыic static final int SAVE EVENT

= 1;

= 32 ;

=

puыic static final int REVERT EVENT = 4 ;

//

Символьные

константы для состояний .

private static BothDirty bothDirty; private static FileDirty fileDirty; private static ParamDirty paramDirty; private static NotDirty notDirty;

private Parameters parameters ; private Button apply , save , revert;

/*

* Этот закрытый конструктор запрещает другим классам

* ( находящимся вне этого класса) создавать экземпляры

*данного класса .

*/

DirtyState ( ) {

if (bothDirty==null) bothDirty = new BothDirty () ; fileDirty ==new FileDirty () ;

paramDirty new ParamDirty () ; notDirty = new NotDirty ( ) ;

/ / i f

/ / constructor ( )

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 )

/* *

* Если данный объект становится текущим состоянием,

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 )

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