/ / Если размер истории превысил значение maxHisto ryLength, / / удаляет из истории> самую старую команду .
if (history . size () maxHistoxyLength) history . removeLast () ;
// addToHistory (AbstractCommand)
//class CommandМanager
Обратите внимание, что класс СопunапdМапаgеr в действительности не исполь зует классы команд, представляющие классы отмены и повтора. Он просто проверяет, реализуют ли их экземпляры интерфейсы Undo или Redo. Эти ин терфейсы представляют собой чистые маркеры-интерфейсы. Приведем лис тинг интерфейса Undo:
interface Undo
} / / inte rface Undo
Исходный код для интерфейса Redo выглядит так же.
Эти маркеры-интерфейсы предназначены для того, чтобы поддерживать класс ComrnandМanager независимым от конкретных подклассов класса AbstractCom mand. Поскольку класс ComrnandМanager обязан управлять отменой и повгором,
все эти классы, представляющие отмену и повтор, дают объекту ComrnandМanager
знать, что он должен отменить или повторить последнюю команду. Лучше всего,
если он будет делать это так, чтобы не требовать от класса ComrnandМanager ка
Кой-либо специальной логики. Тогда должен использоваться механизм, который позволит объекту ComrnandМanager спрашивать, не нужно ли выполнить отмену
или повтор, а не получать извещение об этом. Если можно определить, реализует
лЖети некоторый объект интерфейс Undo или Redo, то объект ComrnandМanager мо спрашивать, нужно выполнить отмену или повгор, ничего не зная о классе,
реализующем этот интерфейс.
И наконец, приведем исходный код для класса UndoComrnand. Класс RedoCom mand отличается от него незначительно.
class UndoCommand extends AbstractCommand implements Undo {
/ * *
* Эта реализация метода dolt на
самом деле ничего не делает .
.. Логика для отмены находится в
классе CommandManage r .
* /
pub11c boolean dolt () {
шаб
332 • Глава 8. Поведенческие шаблоны проектироваНИR
// Этот метод не должен вызываться никогда . throw new NoSuchМethodError () ;
// do I t ( )
/* *
*
*
*
*/
Эта реализация метода undo l t на самом деле ничего не
делает .
Команды undo не отменяют . Вместо этого
выдается команда redo .
public boolean undoIt () {
// Этот метод не должен вызываться никогда .
/throw/ new NoSuchМethodError () ; undo I t ( )
/ / class UndoCommand
Поскольку не должно быть обращений к методам данного класса, эти методы всегда генерируют исключение.
ШАБЛОНЫ ПРОЕКТИРОВАНИЯ,
СВЯЗАННЫЕ С ШАБЛОНОМ COMМAND
Factory Method. Шаблон Factory Method иногда используется для установления
некоторой косвенности между пользовательским интерфейсом и классами ко
манд.
Little Language. Шаблон Command можно использовать как вспомогательное средство при реализации шаблона Little Language. Кроме того, шаблон Little
Language может использоваться для реализации команд.
Marker Interface. Шаблон Marker Interface может использоваться вместе с
лоном Command для реализации обработки команд отмены и повтора.
Snapshot. Если нужно задать упрощенный механизм отмены, согласно которомУ
состояние объекта сохраняется полностью, а не команда за командой, то можно
ИСпользовать шаблон Snapshot.
Template Method. Шаблон Template Method может использоваться для реали
зации логики отмены высшего уровня в рамках шаблона Соmmапд.
в основе шаблона Little Language лежит шаблон Interpreter, описанный в работе
[GoF95], и понятие малого языка, которое было популяризировано Джоном
Бентли в работе [Bentley86]. Более сложные способы проектирования и реали зации языков можно найти в работе [ASU86].
СИНОПСИС
Предположим, необходимо решить несколько похожих проблем и их решения могут быть выражены в виде различных комбинаций немногочисленных эле ментов или операций. Самый простой способ описания решения таких про блем состоит в том, чтобы определить малый язык. Поиск общих, создание
сложных структур данных и форматирование данных - это наиболее часто встречающиеся проблемы, которые решаются при помощи малого языка.
КОНТЕКСТ
допустим, нужно написать программу, которая выполняет поиск в коллекции файлов с целью обнаружения файлов, содержащих заданную комбинацию сим волов или слов. Чтобы не писать отдельную проrpамму для каждой операции поиска, можно определить малый язык, при помощи которого пользователи
опишут комбинации слов, а затем написать одну программу, которая будет на
ходить файлы с комбинациями слов, заданными при помощи малого языка.
Определение языка обычно состоит из двух частей. Синтаксис языка задает
Символы и слова, составляющие язык, и способы их комбинирования. Семантика
ЯЗыка задает значение символов, слов и их комбинаций, входящих в состав
ЯЗыка.
Синтаксис языка обычно определяется rpамматикоЙ. Грамматика - это набор
правил, определяющих последовательность символов, образующих слова и ус
ЛОвные обозначения языка. Кроме того, грамматика содержит правила, кото
РЫе позволяют комбинировать символы и слова языка с целью создания более
Крупных конструкций.
ТIfочное определение семантики большого языка может быть очень сложным
дЛинным. Однако для малого языка иногда вполне достаточно нескольких Простых поясняющих абзацев.
Малый сСКолько
целью
язык можно определить следующим образом. Сначала создается не примеров того, как язык должен выглядеть. Затем примеры обобщаются
получения окончательного определения.
334 • Глава 8. Поведенческие шаблоны проектирования
Следуя этому плану, рассмотрим некоторые моменты, которые полезно учесть
в малом языке при задании комбинаций слов. Основное - это возможность за
давать комбинацию, которая состоит из одного слова. Самый очевидный спо
соб задания для поиска комбинации такого рода заключается в том, чтобы про
сто написать это слово, например, так:
bottle
Кроме того, может появиться необходимость задавать комбинации слов, кото
рые не содержат определенное слово. Это делается просто: перед словом, кото
рого не должно быть в комбинациях слов, нужно поставить слово «not» :
not Ьох
Тогда «Ьох» .
задаются для поиска все комбинации слов, которые не содержат слова
Слова, подобные слову «(not» , которые задают определенное условие, называ ются зарезервированными, поскольку они предназначены для специальных це лей и не могут использоваться таким же образом, как обычные слова. Если слодля во рассматривается как зарезервированное, нельзя задать комбинацию слов поиска, содержащую такое слово, простым написанием этого слова. Однако су ществует способ указания комбинации слов, которая содержит любое произ вольное слово, последовательность слов или знаки пунктуации. Для этого та кую комбинацию слов заключают в кавычки:
" Ye s , not the"
Рассмотрим задание комбинации из двух слов. Очевидно, что синтаксис для та
кой комбинации должен позволять задавать сами слова, ВХОдЯщие в комбина цию. Поскольку комбинировать слова можно по-разному, синтаксис должен
предоставить также способы комбинации слов. для этого необходимо написать
одно слово комбинации, далее указать специальное слово, которое показывает,
как комбинируются слова, а вслед за ним - второе слово комбинации. Напри
мер, чтобы указать комбинации слов, содержащих хотя бы одно из слов «(bottle»
или (1al'» , можно написать следующее:
bottle or j ar
Для задания других способов сочетания двух слов понадобятся дополнительные
слова:
•
(,and» для указания комбинаций слов, содержащих оба слова;
•
«near» для указания комбинаций слов, в которых между указанными слова-
ми могут находиться до 25 каких-то других слов.
Если нужно, например, использовать зарезервированное слово (,and» вместе
с зарезервированным словом (,not» для указания комбинации слов, содержашей ('garlic» и не содержащей <юпiопs» , необходимо будет написать
gar l i c and not onions
Little Language 8 335
Эти примеры охватывают почти все, что нужно для описания комбинаций, со
держащих два слова. При использовании более двух слов потребуется рассмот рение дополнительных условий. Очевидно, что
red алd " pickup t ruck" алd Ьrоkел
означает комбинации слов, содержащих все три элемента: слово «red» , фразу
«pickup truck» и слово «broken».
Если смешиваются разные способы сочетания слов, цель становится неодно значноЙ. Словосочетание
turkey or сhiсkел алd soup
указывает на комбинацию слов, содержащую слово «turkey» или оба слова «chicken» и «SOUP» ? Или оно описывает комбинацию слов, содержащую слово «soup» И хотя бы одно из слов «chicken» или «turkey» ? Чтобы преодолеть эту не однозначность, нужно поставить скобки, определяющие порядок использова ния логических связей в комбинации слов. Для задания первого варианта ин терпретации можно написать:
turkey or ( сhiсkел алd soup )
Для указания второго варианта можно написать:
( tu rkey or сhiсkел) алd soup
Большинству пользователей не нравится, когда их заставляют писать скобки, поэтому желательно найти способ решения проблемы без использования ско бок. Для устранения неоднозначности такого рода в определениях языка широ ко применяется правило nредшесmвованuя, которое назначает приоритеты раз личным используемым в языке операциям. Его суть заключается в том, чтобы
предоставить способ определения порядка операций. Операции с более высо ким приоритетом выполняются перед операциями с более низким приорите
том. Допустим, назначают следующие значения приоритетов:
near 3
and 2 or 1
ПРИ таком определении значений приоритетов словосочетание
mалisол or big леаr house алd rich
задаети комбинации слов, содержашие слово «manison» или оба слова «rich» «big» при условии, что расстояние между словом «big» И словом «house» не
nревышает 25 слов.
Если бы
Образом:
mал i s
использовались скобки,
оn or ( (big near house)
то такое условие выглядело бы следующим
and rich)
Перед тем как спроектировать классы и придать смысл малому языку, важно
Описать грамматику, которая определит синтаксис языка. На основе такого
336
•
Глава
8.
Повед
ен
ческие шаблоны проектирования
описания можно начинать кодирование. Грамматика может быть организована
в соответствии с различными стратегиями. Во всех примерах данного шаБЛОна
используется нисходящая стратегия. Это значит, что начинают с языковой кон
струкции самого верхнего уровня - комбинации слов - и затем определяютдо
все конструкции более низких уровней, которые могут входить в ее состав,
тех пор пока грамматический разбор не будет завершен полностью.
Выше уровня отдельных символов находится уровень конструкций, образуЮ
щих грамматику и называющихся лексемами. Лексемы бывают либо терминаль
ными, либо нетерминальными. Терминальные лексемы соответствуют непре
рывной последовательности символов, а также строкам в кавычках и скобках.
Наприfenceмер, слово в виде
представляет собой терминальную лексему. Конструкции более высокого уров
ня, состоящие из терминальныхлексем, называются нетерминальными лексема ми. Комбинация слов - это нетерминальная лексема.
В большинстве малых языков, включая данный язык словосочетаний, терми нальные лексемы могут отделяться друг от друга символами пробелов, которые не несут смысловой нагрузки.
Правила, позволяющие распознавать последовательности символов как терми нальные лексемы, называются nравuламилексического анализа. Правила, позво ляющие распознавать нетерминальные лексемы в виде последовательностей терминальных и нетерминальных лексем, называются продукциями.
Система обозначений, используемая в данной книге для записи продукций, на зывается фор.!иоЙ Бэкуса-Наура. Согласно этой форме терминальные и нетерми нальные лексемы записываются разными шрифтами, что позволяет различать
их. В этой книге терминальные лексемы указываются так:
quoted_string
а нетерминальные:
combination
Продукция, содержащая нетерминальную лексему и последовательность тер
минальных и нетерминальных лексем, может быть распознана как первая не
терминальная лексема. Приведем пример продукции: combina t i on word
Эта продукция говорит о том, что нетерминальная лексема комбинации моЖет
содержать просто терминальную лексему слова.
Если сушествует несколько последовательностей лексем, которые могут быть
распознаны как нетерминальная лексема, то
л
ь
КО
будет существовать неско
продукций для такой нетерминальной лексемы.
для каждой последовательносТИ.
которая может быть
распозна
на как такая нетерминальная лексема,
е
будет суш
ствоватьдляодна продукция. Например, следуюший набор продукций задает сиН таксис комбинаций, которые содержат или не содержат конкретное слово.
-
Little Language - 337
combina t i on word
combina tion not word
Способ, который используется для определения того, что нетерминальная лек сема должна распознаваться из последовательности лексем неопределенной
Д)1ины, называется рекурсией. Приведем описание набора продукций, который
охватывает почти весь синтаксис предыдущих примеров:
combination ( combina tion )
combina tion simpleCombina tion
combination simpleCombination or combina tion
combination simpleCombination and
combina t ion
combination simрl еСоmbiла tiоn near
combination
simpleCombina tion word
simpleCombinationчто not word для
Обратите внимание, четыре продукции из пяти combina ti оп являются
рекурсивными. Три продукции из этих четырех могли быть записаны при помо
щи combina tion в качестве- первой нетерминальной лексемы и при помощи simpleCombination в качестве второй нетерминальной лексемы. В любом случае они должны соответствовать одним и тем же последовательностям лек сем. Однако для способа реализации, описанного ниже в данном разделе при включении продукций в код, разница существует. Для того способа, который будет рассматриваться, доказано, что при написании продукций лучше всего
использовать правую рекурсию. Правая рекурсия означает, что, если есть воз
можность выбора, в каком месте продукции применить рекурсию, мы должны
выбрать такой вариант, когда рекурсия используется как можно правее.
Хотя этот набор продукций не описывает всех деталей данного языка словосо четаний, содержащейся в нем инФормации достаточно для того, чтобы можно было работать, используя пример, приведенный в книге. Теперь рассмотрим,
каким образом использовать такие продукции для распознавания следующей
Строки как комбинации
fox and not brown
Взглянув на продукции для cOmbina tion, можно увидеть, что combination
Может начинаться либо с открывающей скобки, либо с simpleCombina tion.
Строка начинается с лексемы word. Поскольку строка, которую мы пыаемсяя
распознать как combina tion, не начинается с открывающей скобки, мы пыта
еМСЯ распознать начало строки как simpleCombination. Это соответствует