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

GrandM-Patterns_in_Java

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

Read-Only Interface 8 207

r

ReadOnLyCLient

 

«interface»

Использует

ReadOnLyIF

l-

 

: *- =--- g-:е:tA:--:-tt п:' ь-u:lt--=е- ---j

 

getAttribute2

 

getAttribute3

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

I

 

 

 

 

 

 

 

 

I

 

 

 

 

 

 

 

 

I

 

 

 

 

 

 

 

 

I

 

 

 

 

 

 

 

I

MutatorCLient

I

 

 

МииЫе

 

*

I

 

 

 

 

 

Изменяет.....

getAttributel

 

 

 

getAttribute2

*-----------

 

 

 

setAttributel

 

 

 

 

 

 

 

 

setAttribute2

 

 

 

 

 

 

 

 

getAttribute3

 

 

 

 

 

 

 

 

setAttribute3

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Рис. 6.11. Интерфейс, предназначенный тол ько для чтения

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

ReadOnlyClient. Классы, играющие эту роль, имеют доступ к классу Mutable через интерфейс ReadOnlyIF. Классы, которые обращаются к классу Mutableобъ­ через интерфейс ReadOnlyIF, не имеют доступа к методам, изменяющим екты Mutable.

РЕАЛИЗАЦИЯ

Если класс Mutable должен использоваться как JavaBean, то необходимо

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

Если класс используется как JavaBean, тотот факт, что он реализует интерфейс

ReadOnlyIF, не помешает программистам изменить состояние экземпляров lCЛасса. Это объясняется тем, что при определении, какой из их методов может

быть вызван, JavaBeans не зависят от интерфейсов, которые реализует класс.

для этой цели они используют механизм под названием интроспекции (intro­ Spection).

ЧТОбы определить, какие методы бина могут быть вызваны, интроспекция по­

Лсагается на BeanInfo, связанную с классом бина. Если нет Beanlnfo, связанной

Этим классом, интроспекция создает Beanlnfo по умолчанию. Такая BeanInfo

дает программисту доступ к каждому открытому методу, который находит ин­ l'pоспекция.

private double temperature ;

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

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

она не должна обеспечивать какого-либо прямого доступа к тем методам, кото­

рые изменяют содержимое бина.

СЛЕДСТВИЯ

© Использование шаблона Read-Only InterfaceТОМ, защищает от ошибок програм­ мирования, которые заключаются в что некоторые классы могут изме­ нять те объекты, которые не должны изменять.

®Шаблон Read-Only Interface не защищает объекты от ошибочного измене­

ния в результате злонамеренного программирования. Чтобы защититься от

него, используйте шаблон Protection Proxy, описанный в книге [Grand2001).

ПРИМЕР КОДА

Пример кода для этого шаблона реализует часть проекта, представленноro в разделе «(Контекст» . Листинг класса TemperatureData :

public class TemperatureData implements TemperatureIF

private ArrayList listeners = new ArrayList () ;

/ * *

* Задает значение температуры, хранящееся в этом объекте .

* /

public void setTemperature (double temperature) this . temperature = temperature; fireTemperature () ;

}/ / setTernperature

/* *

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

* /

public double getTemperature ( ) return temperature ;

} / / ge tTernperature ( )

/* *

* Добавляем приемник в этот объект .

* /

Объекты

Read-Only Interface - 209

public void addListener (TemperatureIF listener) ( listeners _ add (listener) ;

} / / addLi stener (Tempe ra ture IF)

/ * * * Удаляем приемник и з этого объекта .

* /

public void removeListener (TemperatureIF listener) (

listeners . remove (listener) ;

} / / removeLis tene r ( TemperatureIF)

/ * *

 

 

*

Отправляем TemperatureEvent

всем

*

зарегистрированным объектам TemperatureListene r .

* /

 

 

private void fireTemperature ()

(

 

int count = listeners . size ( ) ;

 

TemperatureEvent evt;

 

 

evt

= new TemperatureEvent (this , temperature) ;

 

for

(int i = О ; i<count; i++) (

 

TemperatureListener thisListener

 

 

= (TemperatureListener) listeners . qet (i) ;

 

thisListener . temperatureChanqed (evt)

 

} / /

for

 

// f i reTemperature ( )

// class TemperatureData

SensorController записывают значение температуры в объект Теm­ peratureData, вызывая его метод setTemperature. Считается, что никакой другой класс не может обращаться к методу setTemperature. С этой целью

другие классы осуществляют доступ к объектам TemperatureData через ин­

терфейс TemperatureIF:

public interface TemperatureIF

/ * *

*Возвращает прочитанное значение температуры,

*инкапсулированное в этом объекте .

*/

public double getTemperature () ;

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

/* *

* Добавляем приемник в этот объект .

* /

public void addListener (TemperatureIF listener)

/* *

* Удаляем приемник из этого объекта .

* /

public void removeListener (TemperatureIF listener)

/ / interface Temperature I F

Осталось отметить, что интерфейс TemperatureIF не имеет метода setTempe­ rature.

ШАБЛОНЫ nРОЕКТИРОВАНИЯ, СВЯЗАННЫЕ С ШАБЛОНОМ READ-ONLY INTERFACE

Interface. Шаблон Read-Only Interface использует шаблон Interface.

Protection Proxy. Шаблон Read-Only Interface не защищает от злонамеренного программирования. Шаблон Рюtесtiоп Рюху, описанный в книге [Grand2001], может использоваться для обеспечения доступа только для чтения к изменяе­ мому объекту в случае злонамеренного программирования. Правда, достигается это за счет дополнительных издержек ввиду сложности программирования и увеличения времени выполнения.

г А Л В Л

Структурные

шабnоныпроектирования

Adapter (Aдamep) (213)

Iterator (Итератор) (222) Bridge (Мост) (227) Facade (Фасад) (240)

F1yweight (Приспособленец) (248)

Dynamic Linkage (Динамическая компоновка) (260) Virtual Proxy (Виртуальный заместитель) (271) Decorator (Декоратор) (280)

СасЬе Management (Управление кэшем) (287)

Шаблоны , представленные в этой главе, описываютдля распространенные спосо­

бы организации объектов разл ичных типов совместной работы.

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

лагает реализацию определен ного интерфейса, даже если клиент не реализует этот интерфейс.

кШаблон Iterator показывает, каким образом объект может осуществлять доступ

содержимому коллекции объектов, ничего не зная о структуре или классе этой коллекции .

Шаблон Bridge демонстрирует, как можно управлять п араллельн ы м и иерархия­

МИ абстракций и реализаций .

Шаблон Facade описывает, каким образом можно сокрыть в одном объекте всю СЛожность, связанную с использованием группы связанных объектов.

fiaШ аблон Flyweight показывает, как можно избежать больш их расходов па мяти

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

I1спользованию экземпляров, содержащих общие значения.

212 Глава 7. Структурные шаблоны проектирования

Шаблон Dynamic Linkage демонстрирует способ динамического добавления

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

Шаблон Virtual Рroху позволяет отложить на время создание объектов таким способом, который прозрачен для клиентов этих объектов.

Шаблон Оесогаtoг предназначен для динамического расширения или измене­

ния поведения существующих объектов.

Шаблон Cache Management показывает, как можно избежать неоднократного

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

созданного объекта.

Этот шаблон был ранее описан в работе [GoF95].

СИНОПСИС

Класс-адаптер реализует интерфейс, известный его клиентам, и обеспечивает доступ к экземпляру класса, неизвестного его клиентам. Объект-адаптер пре­

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

КОНТЕКСТ

Допустим, создается метод, который копирует массив объектов. Предполагается,

что этот метод должен отфильтровывать объекты, не удовлетворяющие опреде­

ленному критерию, поэтому в копии массива будут содержаться не все элемен­ ты, находящиеся в исходном массиве. Для поддержки многократного исполь­ зования желательно сделать метод не зависящим от конкретного критерия фильтрации. Этого можно достичь, если задать интерфейс, объявляющий ме­ тод, который вызывается копировщиком массива с целью выяснения, будет ли данный объект включен в новый массив (рис. 7. 1).

 

 

Фильтр уетобъекты ДЛЯ него

 

 

 

-I

 

ArrayCopier

....

 

 

«interface»

 

 

-1 :1-----"",,;,,---------::.

-:: __

l

 

 

0. 1

 

__ СОРУFiLtеГIF

 

 

 

 

isCopyable(Object) : boolean

MyCopyfilter isCopyable(Object) : boolean

Рис. 7.1. простой фильтр копирования

Класс ArrayCopier делегирует интерфейсу CopyFilterIF операцию принятия

решения, копировать ли в новый массив элемент старого массива. Если метод i SCopyable возвращает для объекта true, то этот объект копируется в новый

Массив. от­

данный подход решает проблему, связанную с разрешением инкапсуляции в

дельном объекте критерия копирования, используемого объектами ArrayCopier.

214 Глава 7. Структурные шаблоны проектирования

не заботясь о том, что представляют собой объекты копирования. Это решеНИе связано с другой проблемоЙ. Иногда необходимая для фильтрации ЛОГИка находится в методе фильтруемых объектов. Если такие объекты не реализуют интерфейс CopyFi lterIF, то ArrayCopier не может прямо спросить у этих объектов, должны ли они быть скопированы. Однако ArrayCopier может кос­ венно спросить у фильтруемых объектов, должны ли они копироваться, даже если они не реализуют интерфейс CopyFi lterIF.

Предположим, существует класс Document, имеющий метод isVal id, который возвращает результат типа boolean. Нужно, чтобы объект ArrayCopier исполь­ зовал результат обращения к методу isValid с целью выполнения фильтрации во время операции копирования. Класс Document не реализует интерфейс CopyFi lterI F, поэтому объект ArrayCopier не может непосредственно ис­ пользовать объект Document для фильтрации. Другой класс, который реализует интерфейс CopyFi l terIF, не может самостоятельно определить, должен ли объект Document копироваться в новый массив. Это объясняется тем, что у него нет способа получить необходимую информацию, не вызывая метод isValid объекта Document. Решение задачи для этого объекта состоит в том, чтобы вы­ звать метод isValid объекта Document И получить результат (рис. 7.2).

 

 

 

 

 

 

 

<l1liФильтрует объекты ДЛfl него

 

 

 

 

 

 

 

'I --ArrayCopier

 

I

 

 

 

 

 

 

 

«interface»

 

 

 

 

 

 

 

 

 

CopyFiLterIF

 

---1

 

'

 

 

 

. .

 

 

isCopyable(Object) : booLean

 

 

 

 

1

 

 

0

 

1

 

l:!.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

II

 

 

 

 

 

 

 

 

 

 

 

 

I

 

 

 

 

 

 

 

 

 

 

 

 

I

 

Document

 

 

 

 

<l1li

 

 

 

 

 

I

 

 

 

 

 

 

 

 

 

 

I

 

 

 

 

 

 

 

 

 

 

 

 

I

 

 

 

 

 

 

 

ПровеРflет его справедливость

 

 

1

 

I

 

 

 

 

1

 

 

 

 

DocumentCopyFiLterAdapter

 

 

. ..

 

 

 

 

 

 

 

isVaLid() : booLean

 

 

 

 

 

 

 

 

isCopyable(Object) : booLean

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Рис. 7.2. Адаптер фильтра копирования

Согласно этому решению объект ArrayCopier, как всегда, вызывает метод isCopyable объекта, реализующего интерфейс CopyFilterIF. В таком случае этот объект является экземпляром класса DocumentCopyFilterAdapter, кото­ рый реализует метод isCopyable, делегируя вызов методу i sValid объекта

Document.

моти в ы

©Нужно использовать класс, который вызывает метод через интерфейс, при­

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

этот интерфейс. Изменение класса таким образом, чтобы он реализовЫВaJ1

интерфейс, неприемлемо по двум причинам.

@)

Adapter 215

1. OrcyrcTByeT исходный код для класса.

2.Класс является классом общего назначения и не подходит для реализа­ ции интерфейса специального назначения.

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

РЕШЕНИЕ

Предположим, есть класс, который вызывает метод через интерфейс. Нужно, чтобы экземпляр этого класса вызывал метод объекта, не реализующего такой интерфейс. Можно сделать так, что экземпляр класса будет производить вызов через объект-адаптер, который реализует интерфейс, делегируя вызовы методу объекта, не реализующего интерфейс (рис. 7.3).

 

 

 

 

 

 

 

 

 

 

 

 

 

I

Использует

 

«interface»

 

I

 

Client

 

TargetIF

 

 

I

 

 

 

InterfaceMethodO

 

 

 

 

 

 

 

I

 

 

 

 

Adaptee

 

Использует

 

 

 

 

 

 

 

otherMethodO. . .

 

 

 

Adapter

 

 

 

 

1

 

1

 

 

 

interfaceMethodO

 

 

 

 

 

 

 

 

 

 

Рис. 7.3. Адаптер

Опишем роли, исполняемые этими классами и интерфейсом.

Qient. Класс, выступающий в этой роли, вызывает метод другого класса через Интерфейс, чтобы ничего не знать о реальном классе, реализующем этот метод.

еTargetlF}! . Данный интерфейс в этой роли объявляет метод, который вызывает­ классом Cl ient.

Adapter. Этот класс реализует интерфейс TargetIF. Он реализует метод, вы­

гоЗЫваемый клиентом, делегируя вызов методу класса Adaptee, не реализующе­

интерфейс TargetIF.

AdapteeНо . Класс, играющий эту роль, не реализует метод интерфейса TargetIF,

содержит метод, который хочет вызывать класс Cl ient.

I<.ласс Adapter может не только просто делегировать вызов метода. Он может

ВЫполнять некоторые преобразования аргументов, а также предоставлять до­

Полнительную логику для сокрытия различий между подразумеваемой семан­ tl1кой метода интерфейса и реальной семантикой метода класса Adaptee.

216 Глава 7. Структурные шабланы праектиравания

Сложность класса-адаптера ничем не оrpаничена. До тех пор, пока основным назначением класса является использование его в качестве проме очноro звена при вызове методов другого объекта, его можно рассматривать ка« класс-адаптер.

РЕАЛИЗАЦИЯ

Реализуется класс-адаптер очень просто. Однако при реализаuии шаблона Adapter один вопрос требует внимательного рассмотрения: откуда объекты-адап­ теры узнают, какой экземпляр класса Adaptee вызывать. Здесь сушествуют два подхода.

1 . Передать ссылку на объект Adaptee в качестве параметра для конструктОра объекта-адаптера или для одного из его методов. Это позволяет использо­ вать объект-адаптер с любым экземпляром или с любым возможным коли­ чеством экземпляров класса Adaptee. Такой подход проиллюстрирован в следуюшем примере:

class CustomerBillToAdapter implements AddressIF ( private Customer myCustomer;

public CustomerBillToAdapter= (Customer customer) { myCustomer customer;

} // con s t ructor

public Strinq qetAddressl ()

return myCustomer . qetBillToAddressl () ;

public void setAddressl (Strinq addressl) ( myCustomer . setвillToAddressl (addressl) ;

//setAddressl ( String)

}// class CustomerBi l lToAdapter

2.

Сделать класс-адаптер внутренним классом класса Adaptee. При этом уп­ рошается ассоuиаuия между объектом-адаптером и адаптируемым объек­ том. Кроме того, ассоuиаuия при этом становится жесткой. Данный подход показан в следуюшем примере:

MenuItem exit = new MenuItem(caption) ; exit. addActionListener (new ActionListener ()

public void actionPerformed (ActionEvent evt) close () ;

// actionPerformed (ActionEvent )

) ;

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