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

GrandM-Patterns_in_Java

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

Filter _ 187

Программа вполне может использовать обе формы шаблона Filter. Однако один и тот же класс обычно не участвует в обеих формах этого шаблона.

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

ПРИМЕНЕНИЕ В JAVA API

Пакет j ava . io содержит класс Fi l te rReader, который принимает участие в шаблоне Filter в качестве абстрактного класса-фильтра источника. Соответст­ вующий абстрактный класс источника называется Reader. Конкретные под­ классы класса Filte rReader - это BufferedReader, FileReader и LineNum­ berReader. Не существует отдельного интерфейса, который бы выполнял роль SourceIF. Эту роль взял на себя тот же класс Reader.

Пакет j ava . io содержит класс FilterWriter, который участвует в шаблоне

Filterный как абстрактный класс-фильтр приемника. Соответствующий абстракт­ класс приемника - это Writer. Конкретные подклассы класса Fil­ terWri te r - Buffe redWriter, Fi leWriter и PrintWriter. Кроме того,

класс Writer исполняет также роль SinkIF.

Приведем обычную организацию объектов класса Fil terReader в программе, которая читает строки текста (команды) и отслеживает номера строк для выда­ чи сообщений об ошибках:

LineNumberReader in; void init (String fName)

FileReader fin;

try {

 

 

fin = new FileReader (fName) ;

 

in =

new LineNumberReader (new BufferedReader (fin» ;

catch

(FileNotFoundException

е) {

System . out . println ("Unable to open "+fName) ;

ПРИМЕР КОДА

в качестве примера классов, реализующих риН-форму шаблона Filter, опишем lQJaccbI, которые читают и фильтруют[i байтовые потоки. Первым приведем lQJacc, участвующий в шаблоне Filter качестве абстрактного источника:

public interface InStreamIF { /**

* Прочитать байты и з байтового потока и записать и х в массив .

188 Глава 6. Разделяющие шаблоны проектирования

*

@param array Заполняемый массив .

*

@ return Если байтов недостаточно для заполнения

*

массива , то этот метод

завершается , заполнив массив

*

реальным количеством байтов . Возвращает общее количество

*

байтов или - 1, если достигнут конец потока данных .

*

@ throws IOException Если появляется ошибка ввода-вывода .

public int read (byte [ ] array)

throws IOException;

//

interface InStreamIF

 

А теперь приведем класс, который реализует интерфейс InStreamIF и участвует

в шаблоне Filter в качестве источника: /**

* Этот класс считывает поток байтов и з файла . */

public class FileInStream implements InStreamIF {

private RandomAccessFile file ;

 

 

/**

 

 

 

 

*

 

Cons tructor

 

 

*

 

@param fName Имя считываемого файла .

 

*/

 

 

 

 

public FileInStream(String fName) throws

IOException

 

file =

new RandomAccessFile (fName ,

"r") ;

} // Const ructor ( String)

 

 

/**

 

 

 

 

*

 

Считывает байты и з файла и заполняет

этими байтами массив .

* /

 

 

 

 

public int read (byte [ ] array) throws

IOException {

 

return

file . read (array) ;

 

 

}

// read

(byte [ J )

 

 

} //

class FileInStream

 

 

Следующий класс принимает участие в шаблоне Filter в качестве фильтра абстрактного источника:

public abstract class FilterInStream implements InStreamIF {

private InStreamIF inStream;

/ * *

 

*

Cons t ructor

*

@param inSt ream

*

Объект InStreamIF, которому данный объект должен

*

делегировать операции чтения .

*/

 

pub1iC FilterInStream(InStreamIF inStream) throws=IOException

this . inStream inStream; // Con s t ructor ( I nS treamIF)

/* *

* Считывает байты из байтового потока и заполняет ими массив .

*/ pub1ic int read (byte [] array) throws IOException {

return inStream. read (array) ;

// read (byte [ ] )

//class Fi lterlnStream

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

pub1ic c1ass ByteCountInStream= extends Fi1terInStream { private 10n9 byteCount О ;

/ * *

*Constructor

*@param inSt ream

* InStream, которому данный объект должен делегировать

*операции чтения .

*/ pub1ic ByteCountInStream(InStreamIF inStream)

throws IOException super (inStream) ;

// Constructor ( InStream)

/ * * * Считывает байты и з байтового потока в массив . * / pub1ic int read (byte [ ] array) throws IOException {

int count;=

count super . read (array) ; if (count >0)

byteCount += count; return count;

// read (byte [ ] )

190 Глава 6. Разделяющие шаблоны проектирования

1*

*

 

*1

Возвращает количество байтов , считанных этим объектом .

*

 

public long getвyteCount ( )

 

return byteCount;

11

11 getByteCount ( )

class ByteCountlnStream

и наконец, приведем класс-фильтр, который выполняет преобразование кодов

символов потока байтов:

1

**

 

*

 

Этот класс рассматривает каждый байт байтового потока

*

 

как

восьмибитовый код символа и преобразует его в другой

*

 

код символа , используя для этой цели таблицу

*

 

преобразованиЙ .

* 1

 

 

public class TranslateInStream extends FilterInStream {

 

 

private byte [] translationTable ;

 

 

private final static int ТRANЗ TBL LENGTH = 256 ;

 

 

1*

*

 

 

*

Constructor

 

 

*

@param inStream

 

 

*

Объект InSt reamIF, которому данный объект должен

 

 

*

делегировать операции чтения .

 

 

*

@pa ram table

*Массив байтов , используемый с целью определения значенИЙ

*преобразования для символьных кодов . Меняет символ

*с кодом n на п-й элемент таблицы преобразования .

*Если длина массива превышает количество элементов , задаваемое TRANS_TBL_LENGTH, то дополнительные элеменТЫ

*игнорируются . Если массив меньше TRANS TBL LENGTH

*элементов, то преобразование не производится для тех

*символов , коды которых больше или равны длине массива

*1 table .

public TranslateInStream(InStreamIF inStream,

11

byte [ ] table) throws IOException {

 

super (inStream) ;

11

Создает таблицу преобразования путем копирования

данных преобразования .

Java

Filter _ 191

translationTable = new byte [TRANS_TBL_LENGTH] ; Syste m . arraycopy (table, О , translationTable, О , мath .min (TВANS_mL_LENGTH,

= table . lenqth» ;

for (int i table . lenqth; i < TRANS_TBL_LENGTH; i++) {

translationTable [i] = (byte) i;

//for

//Constructo r ( InStream)

public int read (byte [ ] array)

throws IOException (

int count;

 

 

 

count = super . read (array) ;

 

for (int i

=

О ; i < count;

i++) {

array [i]

=

translationTable [array [i] ] ;

}// for return count;

}// read ( byte [ j )

// class ByteCountlnStream

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

Composite. Шаблон Composite может служить альтернативой шаблону Filter. Он

позволяет представлять данные в виде древовидных структур.

аPipe. Шаблон Pipe иногда выступает в качестве альтернативы шаблону Filter,

иногда используется вместе с ним.

Описание шаблона Pipe приводится в работе [BMRSS96]. Подобно шаблону

Filter, шаблон Pipe позволяет объекту, представляющему источник данных, от­

Правлять поток данных объекту, представляющему приемник данных. Вместо перемещения данных, инициируемых объектом источника или приемника, они

действуют асинхронно по отношению друг к другу. Объект источника записы­ вает данные в буфер, когда захочет. Приемник считывает данные из буфера,

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

приемник ожидает появления данных в буфере.

API содержит классы j ava . io . PipedReader и j ava . io . PipedWriter,

КОторые вместе реализуют шаблон Pipe.

{)ecorator. Шаблон Filter специальный случай шаблона Decorator, в котором

ilогики для управления потоком данных.

-

Объект источника или приемника данных достраивается с целью добавления

Шаблон Composite известен также как шаблон Recursive Composition (Рекур_ сивная композиция). Ранее он был описан в работе [GoF95].

СИНОПСИС

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

Шаблон Composite представлен в книге с ТОЧКИ зрения рекурсивного построения сложного объекта, состоящего из других объектов. Composite относится к раз­ деляющим шаблонам, поскольку в процессе создания проекта этот щаблон часто используется для рекурсивного разложения составного объекта на более простые объекты.

КОНТЕКСТ

Предположим, что создается программа форматирования документа. Она фор­ матирует символы, представляя их в виде строк текста, которые образуют колонки, разделенные на страницы. Однако документ может содержать и дру­ гие элементы. Колонки и страницы могут иметь рамки с колонками внутри. Колонки, рамки и строки текста могут содержать рисунки (рис. 6.4).

Очевидно, что сложностей здесь достаточно. Объекты Page и Frarne должнЫ

знать, как обрабатывать и комбинировать элементы двух видов. Объекты

Colurnn должны знать, как обрабатывать и комбинировать элементы трех ви­ дов. Шаблон Composite устраняетданную сложность, позволяя таким объектам управлять элементами только одного вида. Этого можно достичь, если классЫ всех элементов документа будут реализовывать общий интерфейс. На рис. 6.5 показано, как можно упростить отношения между классами элементов доку­ мента при помощи шаблона Composite.

При использовании шаблона Composite вводится общий интерфейс для всех элементов документа и общий суперкласс для всех контейнерных классов. ПРII

этом количество отношений агрегации уменьшается до одного. Теперь управ­ ление агрегацией - обязанность класса Cornpos iteDocurnentElernent. Кон­ кретные контейнерные классы (Docurnent, Page, Colurnn и др.) должны знаТЬ только, как соединять элементы одного вида.

Composite _ 193

1 ..*

1 ..*

1 ..*

1 ..*

Character

Рис. 6.4. Отношения между контейнерными классами документа

 

 

 

 

 

 

 

I

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

«interface.

 

Ikr -____--.

 

 

 

 

 

 

 

 

 

 

 

DocumentELementIF

 

 

 

 

·

 

 

 

 

 

.

 

 

.L

r

 

 

.

I

 

I

 

 

 

 

 

 

 

 

 

CompositeOocumentE/em ent

 

 

 

,• _ .. __________ .. __ .. __ .. __ ,..,___________

 

________________ ..

 

 

 

 

 

·

 

 

 

 

 

.

 

 

 

 

 

 

.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Character

 

 

 

 

 

Image

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

r

I

1

 

I

 

I

I

I

 

I

 

I

I

 

I

I

I

I

Document

 

Page

СОlumп

Frame

LineDfТext

 

Рис. 6.5.

 

Документ, использующий шаблон Composite

 

оти в ы

Есть сложный объект, который нужно представить в виде иерархии объек­

тов , представляющей отношение «(часть - цел ое».

 

 

 

 

Н

ужно с вести

к м и н и муму с

ожность

иерархии

 

част

е

ое

 

остав яя

 

 

 

л

«

» ,

м ин и мал ЬНЫ;>1

 

 

 

 

 

ь - ц л

 

л

кол ичество разли чных дочерни х объектов, о которых долж­

ны быть осве

ом

л

ены объекты

ерева.

 

 

 

 

 

 

 

 

д

 

 

д

 

 

 

 

 

 

 

Не предъя вляется никаких требований к бол ьше й части объектов из иерар­

хии по их разл ич и ю.

РЕШЕНИЕ Ие.

Свести к м и н и муму сложность составного объекта, организованного в виде рархии «часть - целое» . Это выполняется путем предоставления и нтерфейса' который должен реализовываться всеми объектами , входящими в иерархию и абстрактного суперкласса для всех составных объектов этой иерарX}f

(рис. 6.6).

«interface» ComponentIF

operationO *

. . .

I

Componentl

operation()

. . .

 

Abstract Component

...

I

j

 

Component2

.

 

 

 

. .

 

AbstractComposite

 

 

 

 

 

 

 

operationO

 

 

add(AbstractComponent)

 

 

.. .

 

 

 

remove(AbstractComponent)

 

 

 

 

 

getChi1d(int)

 

 

 

 

 

 

 

 

I

 

I

I

ConcreteCompositel

 

ConcreteComposite2

 

.. .

 

 

 

 

 

 

 

 

 

 

operationO

 

operationO

 

 

. . .

 

. . .

 

 

Рис. 6.6. Отношения между классами составного объекта

Опишем интерфейс и классы, принимающие участие в шаблоне Composite.

ComponentlF. Интерфейс, выступающий в этой рол и , реализуется всеми объек'

тами , входящими В иерархию и образующими составной объект. Как правИЛО,

составные объекты рассматривают содержащиеся в них объекты как экземПне­ ляры классов, реализующих и нтерфейс ComponentIF, а не как экземпляры которых реал ьно существующихи Т.Д. классов.

Component1, Component2 Экземпляры этих классов считаются лисТЬЯМ"

дерева в древовидной структуре.

AbstractComposite. Класс, выступающий в этой рол и , представляет собой аБСТ­ рактный супер класс для всех составных объектов, прини мающих учаСТI1е в шаблоне Composite. AbstractComposi te определяет и предоставляет реаЛl1-

заци и методов , задаваемых по умолчанию и предназнач енных для управлеНl111

компонентами составного объекта. Метод add добавляет компонент в состаfl8в­

ной объект, а метод remove - удаляет. Метод getChi ld возвращает ссылку

объект компонента соста вного объекта.

CompoSi е 8 y

ConcreteCompositel, ConcreteComposite2 и Т.д, Экземпляры этих классов пред­

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

IU1acca AbstractComponent.

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

ного дерева (рис. 6.7).

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

...Содержит

Содержит '"

 

 

:ConcreteComDositel

 

 

Содержит ...

 

 

 

 

 

 

 

 

 

 

 

Содержит ...

 

 

 

 

 

 

 

 

 

 

 

 

:ConcreteComDonentl

:СопсreteСоmропепtЗ

:ConcreteComDosite2

:ConcreteComponent2

 

 

 

 

Содержит'"

:ConcreteComponent5

Рис. 6.7. Составной объект класса ConcreteCompositel

РЕАЛИЗАЦИЯ

Если классы, участвующие в шаблоне Composite, реализуют какие-либо опера­

ции, делегируя их своим родительским объектам, то для повышения произво­ Дительности и простоты использования можно добавить в каждый экземпляр l(JIacca AbstractComponent ссылку на своего родителя. При реализации опе­ Рации получения ссылки на предкаи важно сделать это так, чтобы обеспечить

СОгласованность между предком потомком. Объект ComponentIF идентифи­

Цирует объект AbstractComposite как своего родителя тогда и только тогда,

I<огда AbstractCompos i te идентифицирует его как одного из своих потомков. liаилучший способ реализации этого механизма состоит в изменении ссылок lia родителей и потомков только при помощи методов добавления и удаления,

задаваемых в классе AbstractCompos ite.

Совместное использование компонентов многими родителями при помоши l!Jаблона Flyweight (см. гл. 7) - это способ сохранения памяти. Однако совме-

196 Глава 6. Разделяющие шаблоны проектирования

стно используемым компонентам сложно поддерживать ССbVlки на родителей надлежащим образом.

Класс AbstractCompos i te может предоставлять составным объектам задавае_ мую по умолчанию реализацию управления потомком. Следует знать, однако, что очень часто классы конкретных составных объектов замещают задаваемую по умолчанию реализацию.

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

СЛЕДСТВИЯ

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

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

©Если клиент вызывает метод объекта ComponentIF, который, как ожидает­ ся, должен выполнить некоторую операцию, и объект ComponentIF являет­

ся объектом AbstractComposite, то AbstractCompos i te можетделегиро­

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

Component IF, который не является AbstractComposi te, и метод нуждает­

ся в некоторой контекстной информации, то объект Component I F делеги­

рует запрос на получение контекстной информации своему родителю.

Некоторые компоненты могут реализовывать специфические для этоГО

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

символов и изображений. Логично иметь метод getFont для СИМВОЛЬНЬ!J(

элементов документа. Элементы изображений документа не нуждаютСЯ

вметоде getFont. Основное удобство шаблона Composite заключаетсЯ

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

имеют дело. Чтобы позволить другим классам вызывать getFont, не имеЯ информации об определенном классе, с которым они имеют дело, все об01ь­

екты, входящие в состав документа, могут наследовать метод getFont

DocumentElemen t I F. В целом, при использовании шаблона Compos ite

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