![](/user_photo/2706_HbeT2.jpg)
GrandM-Patterns_in_Java
.pdf![](/html/2706/346/html_MxkKzTLKJz.Ja4Q/htmlconvd-LkZgZ6311x1.jpg)
318 • Глава 8. Паведенческие шабланы проектирования
Классы, которые расширяют класс Sensor, вызывают метод noti fy. Этот Ме тод они наследуют от него с целью передачи измеренного значения объекту, который отвечает за обработку измерений. Классы, которые расширяют класс AbstractSecuri tyZone, отвечают за обработку измеренных значений, полу_ ченных от соответствующего объекта Sensor.
Приведем пример кода для классов, показанных на рис. 8.3. Первым пред ставлен код для класса TemperatureSensor. Обратите внимание, что класс Temperature Sensor ничего не делает с данными, полученными от темпера_ турного сенсора, а передает их дальше.
class TemperatureSensor extends Sensor ( private SecurityZone zone ;
/ * * |
|
|
* |
Если датчик температуры, |
связанный с объектом, |
* |
фиксирует изменение температуры, вызывается метод notify . |
|
* / |
|
|
void notify (int measurement) |
( |
zone . notify (measurement, this) ;
}/ / not ify ( in t )
// class TemperatureSensor
Все классы, которые моделируют зоны безопасности, реализуют интерфейс
SecurityZone I F :
public interface SecurityZoneIF (
/* *
* Этот метод вызывается для оповещения зоны контроля
* безопасности об изменении значения сенсора .
* /
public void notify (int measurement, Sensor source)
/* *
* Этот метод вызывается дочерней зоной для сообщения о пожаре .
* /
public void fireAlarm(SecurityZone zone)
/ / interface SecurityZone IF
Следуюший код - для класса SecurityZone, который является суперклассоМ
ДЛЯ всех классов, образующих цепочки ответственности в этом примере:
abstract class SecurityZone implements SecurityZoneIF { private SecurityZone parent;
![](/html/2706/346/html_MxkKzTLKJz.Ja4Q/htmlconvd-LkZgZ6312x1.jpg)
![](/html/2706/346/html_MxkKzTLKJz.Ja4Q/htmlconvd-LkZgZ6313x1.jpg)
320 • Глава 8. Поведенческие шаблоны лроектирования
Теперь КОДЫ подклассов класса Securi tyZone, рассмотренных в разделе «(Кон
текст» :
class Лrеа extends SecurityZone (
/* *
* Этот метод вызывается методом not ify,
* поэтому объект может обработать измерения .
* /
boolean handleNotification (int measurement , Sensor sensor) { if (sensor instanceof> TemperatureSensor)
if (measurement 150) fireAlarm(this) ; return true;
|
I / |
i f |
|
/ / i f |
|
|
return |
false ; |
|
/ / handleNoti ficat ion ( int, Sensor) |
|
/ / |
class Area |
|
class Warehouse extends SecurityZone ( |
||
/ * * |
|
|
* |
Этот метод вызывается методом not i fy, |
*поэтому объект может обработать измерения .
*/
protected
boolean handleNotification (int measurement, Sensor sensor) {
return false ;
/ / handleNotification ( int , Sensor)
puыlcc void fireAlarm(SecurityZone zone) if (zone instanceof Лrеа) (
//Включаем разбрызгиватели в близлежащих областях .
// Не вызываем super . fireAlarm, так как при этом
1/ включаются разбрызгиватели по всему складу . if (getParent () ! = null)
getParent () . fireAlarm(zone) ; return ;
Chain af Respansibility - 321
} 11 i f
super . fireAlarm (zone} ;
11 f i rеАlаrm ( Sесuri t уZоле ) 1 1 class Warehouse
ШАБЛОНЫ ПРОЕКТИРОВАНИЯ, СВЯЗАННЫЕ
С ШАБЛОНОМ CHAIN OF RESPONSIBILIТY
Composite. Если цепочка объектов, используемая шаблоном Chain of Responsi bility, является частью более крупной структуры, то эта структура создается при помощи шаблона Composite.
Command. Шаблон Chain of Responsibility не идентифицирует объект, выпол ектняющий. команду, а шаблон Command явно и конкретно определяет такой объ
сяTemplate Method. Если объекты, образующие цепочку ответственности, являют то частью более крупной структуры, созданной при помощи шаблона Composite, шаблон Template Меtlюd может использоваться для планирования поведения
отдельных объектов.
![](/html/2706/346/html_MxkKzTLKJz.Ja4Q/htmlconvd-LkZgZ6315x1.jpg)
Этот шаблон ранее был описан в работе [GoF95].
СИНОПСИС
Шаблон Command инкапсулирует команды в объекте таким образом, что мож
но управлять их выбором и последовательностью, ставить их в очередь, отме
нять их и выполнять иные манипуляции.
КОНТЕКСТ
Предположим, нужно спроектировать программу обработки текста таким обрцае зом, чтобы она могла выполнять какие-то действия или отменять их. С этой лью реализуется действие в виде объекта, имеюшего методы do И undo (рис.
|
|
|
|
|
AbstractCoттand |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
dolt() |
|
|
|
|
|
|
I |
|
|
undoIt() |
|
|
|
I |
|
|
|
|
|
|||||
|
|
|
|
DeteteCommand |
|
||||
|
|
|
|
||||||
|
|
InsertStringCommand |
|
|
|
" |
|||
|
«constructorn |
|
«constructon> |
|
|
||||
|
InsertStringCommand(position:int, string:String) |
|
DeteteCommand(position:int, tenght:int) |
|
|
||||
|
«misc» |
|
«misc» |
|
|
||||
|
doIt() |
|
doltO |
|
|
||||
|
undoItO |
|
undoItO |
|
|
||||
|
|
|
|
|
|
|
|
|
|
Рис. 8.4. Классы do и undo
Если дать текстовому процессору задание, вместо непосредетвенного выполне
ния команды он создаст экземпляр подкласса класса AbstractCorrunand, соот
ветствующий команде. Текстовый процессор передает всю необходимую ИН
формацию конструктору этого экземпляра. Например, если поступает комаНдаti
вставить один или более символов, он создает объект InsertStringCorrunand
передает конструктору объекта ту позицию в документе, где нужно сделатЬ вставку, и строку, которую нужно вставить.
Если процессор обработки текста однажды «материализовал» команду в виде объекта, ДЛЯ выполнения этой команды он вызывает метод do l t этого объекта.
![](/html/2706/346/html_MxkKzTLKJz.Ja4Q/htmlconvd-LkZgZ6316x1.jpg)
324 • Глава 8. Поведенческие шаблоны проектирования
обрашаясь к методу doI t класса. Обрашение к логике отмены команды ОСУЩе
ствляется посредством вызова метода undo I t класса.
Конструктор объекта обычно предоставляет любые параметры, неоБХОДИМЫе
для команды. Большинство команд требуют, по крайней мере, одного парамет
ра - объекта, на который воздействует команда. Например, команда сохране
ния объекта на диске обычно требует, чтобы сохраняемый объект передавался
конструктору объекта команды.
Invoker. Если нужно вызвать команду, класс, исполняюший эту роль, создает конкретные объекты команд. Он может вызвать метод doI t этих объектов или оставить эту работу для объекта CommandManage r.
CommandManager. Класс CommandManager отвечает за управление коллекцией объектов команд, созданной объектом Invoker. В круг конкретных обязанно стей класса CommandManager может входить управление командами отмены
и повтора, упорядочение и планирование команд.
Классы СоrпmапdМапаgеr обычно не зависят от тех приложений, которые их
применяют, и могуг использоваться очень часто.
РЕАЛИЗАЦИЯ
При реализации шаблона Command необходимо рассмотреть несколько вопро сов. Первый и, возможно, самый важный заключается в том, чтобы решить, ка кими будут команды. Если команды вьщаются пользовательским интерфейсом, предоставляюшим команды пользовательского уровня, то вполне естественный способ идентификации классов конкретных команд состоит в том, чтобы иметь
конкретный класс команды для каждой команды пользовательского УРОВНЯ.
Если придерживаться этой стратегии и при этом имеются некоторые особенно
сложные пользовательские команды, то сложность классов команд будет соот
ветствуюшеЙ. Чтобы избежать излишнего усложнения одного класса, нужно реализовывать более сложные команды пользовательского уровня при помошИ нескольких классов команд.
Если количество внешних команд или команд пользовательского уровня слишком
велико, то можно использовать стратегию, согласно которой они реализуютСЯ
при помоши комбинаций объектов команд. Эта стратегия позволяет реализо
вать большое количество внешних команд при помоши меньшего количества
классов команд.
U n d o/re d o
При реализации необходимо рассмотреть еше один вопрос: получение инфор
мации о состоянии, необходимой для команд отмены (undo). Чтобы отменЯТЬ
результаТbl действия команды, нужно сохранять достаточную часть состоянИЯ
объектов, на которые она воздействует; тогда возможно восстановление этого состояния.
Command 8 325
Бывают команды, отменить которые невозможно, так как ДЛЯ этого нужно со хранять огромное количество информации о состоянии. Например, команда глобального поиска и замены иногда может изменять такое большое количест
во информации, что поддержка всей первоначальной информации потребует недопустимо большого объема памяти. Некоторые команды вообше нельзя от менять из-за невозможности восстановления состояния, измененного этими
командами, например, команды удаления файлов.
Объект CommandМanager должен знать, когда выполняемая команда не может б•ыть отменена. Это объясняется рядом причин.
•ложить ему не выполнять команду.
Сохранение истории команд с целью их отмены требует памяти, а иногда
идругих ресурсов. После выполнения команды, которая не может быть отме нена, история команд может быть удалена. Сохранение истории команд после
•выполнения неотменяемой команды требует ненужного расхода ресурсов. Большинство пользовательских интерфейсов программ, имеющих команду отмены, содержат в меню пункт, который пользователь может выбрать для отмены команды. Для пользователя будет неприятным сюрпризом, если он попытается отменить действие, а в ответ получит сообщение, что последняя команда не может быть отменена. Чтобы не раздражать пользователя, объ ект управления командой должен разрешать или запрещать в меню пункт отмены, если последняя выполненная команда, соответственно, может или не может быть отменена.
ЕслВ и не нужно поддерживать операции отмены, можно упростить шаблон.
таком случае класс AbstractCommand не должен определять метод undol t.
Ка к и з б еж а т ь з а в и с им о с т и
о т п ол ьз о в ател ь с к и х и нтер ф е й с о в
Существует широко распространенное расширение шаблона Command, приме
Няемое в том случае, когда команды выдаются пользовательским интерфейсом.
Цель этого расширения шаблона заключается в том, чтобы избежать привязки
КОмпонентов пользовательского интерфейса к конкретному объекту команды Или даже не позволить компонентам пользовательского интерфейса знать о ка КИх-либо конкретных классах команд. Расширение предусматривает встраива Ние имени команды в компоненты пользовательского интерфейса и использо
Вание шаблона Factory Method для создания объектов команд (рис. 8.6).
В таком случае классы GUI-компонентов ссылаются на имя команды, которую
они вызывают, а не на класс команды, реализующий эту команду, и не на
![](/html/2706/346/html_MxkKzTLKJz.Ja4Q/htmlconvd-LkZgZ6319x1.jpg)
![](/html/2706/346/html_MxkKzTLKJz.Ja4Q/htmlconvd-LkZgZ6320x1.jpg)
Command • 327
©Возможность создания КOJшекций и управления последовательностью ко маНд означает, что шаблон Command может быть положен в основу меха низма, поддерживающего макросы клавиатуры. Такой механизм позволяет записывать последовательность комаНд и позднее воспроизводить ее. Кро ме того, шаблон Command может использоваться для создания сложных шаблонов других видов.
©Добавление новых команд обычно не вызывает затруднений, так как не на рушает каких-либо зависимостей.
ПРИМЕНЕНИЕ В JAVA API
В Java АР! нет каких-либо удачных примеров шаблона Command. Но оно со
держит некоторое подобие шаблона Command в JFC (Java Foundation Classes,
библиотека базовых классов Java). Классы кнопок или пунктов меню имеют методы getAct i onCommand и setActionCommand, которые можно использо вать для получения и задания имени комаНдЫ, связанной с кнопкой или пунк том меню.
ПРИМЕР КОДА
Продолжим рассмотрение примера комаНд отмены и повтора для текстового процессора, представленного в разделе «Контекст».
На рис. 8.7 изображена диаграмма стандартного взаимодействия, в ходе кото рого создаются и выполняются комаНдЫ.
|
|
|
1: create(dacument:Dacument, |
|
|||||||||
|
|
|
|
pasitian:int, |
|
|
|
|
|
|
|
|
|
|
|
|
string:String) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
command:InsertStringCammand |
{new} |
||||||
|
|
|
|
|
|
|
|
||||||
|
|
|
|
|
1.1: invoceCommand(command) |
|
|
|
|||||
|
|
|
|
|
|
|
|
||||||
1.1.2[ok): addTaHistory(command) ,--__________....1 |
|
|
|||||||||||
«seLf» |
|
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
|
|
|
|
1.1.1: ok:-dоItО |
|
|
Рис. 8.7. ВзаимодеДЛЯ йствие в шаблоне Command
текстового процессора
Объект создает экземпляр класса InsertStr ingCommand, передавая его конст
руктору документ, в который нужно вставить строку, саму строку и позицию,
куда ее надо вставить. После инициализации объекта InsertStringCommand
конструктор вызывает метод invokeCommand объекта CommandМanager. Метод