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

GrandM-Patterns_in_Java

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

Morker Interface - 95

private synchronized LinkedList findEq (Object target) {

)// find (Obj ect)

/** * Найти в связном списке объект, равный данному объекту .

*Равенство определяется путем вызова метода

*equals данного объекта .

*/

private synchronized LinkedList findEquals (Object target) {

} // find (Obj ect)

) // class LinkedList

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

С ШАБЛОНОМ МARKER INTERFACE

Snapshot. Шаблон Marker Interface является частью шаблона Snapshot и ис­ пользуется ДII Яопределения возможности сериализации объектов.

Polymorphism. Шаблон Polymorphism (описанный в книre [Grand99]) представ­ ляет собой альтернативный способ изменения поведения при вызове метода.

Рroху - очень распространенный шаблон, используемый многими другими

шаблонами, но никогда не применяющийся сам по себе. Шаблон Рroху был

описан ранее в работе [GoF95] .

СИНОПСИС

Шаблон Рroху заставляет обращаться к объекту косвенно. Обращение к мето­

дам этого объекта делегируется через объект-заместитель. Здесь имеется в виду,

что запросы к реальному объекту идут через объект-заместитель. Классы для

объектов-заместителей объявляются таким образом, что клиентские объекты не знают, что они имеют дело с заместителем.

КОНТЕКСТ

Объект-заместитель - это объект, методы которого вызываются на правах другого объекта. Клиентские объекты вызывают методы объекта-заместителя, которые выполняют действия, ожидаемые клиентами, не напрямую. Они вызывают ме­ тоды объекта, который обеспечивает реальный сервис (рис. 4. 1 8).

1: doItO 1.1: doItO

:ServiceProxv

Рис. 4.18. Обращения к методом через объект-заместитель

Хотя методы объекта-заместителя не прямым образом обеспечивают сервис,

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

ление такими сервисами. Объекты-замест и вместе с объектами, предостав­

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

прямой доступ к объектам, предоставляющим сервис, или они обращаются

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

рез общий интерфейс, чем через экземпляр некоторого конкретного класса.

Поэтому клиентские объекты могут не знать о том, что они вызывают методЫ:

объекта-заместителя, а не объекта, реально обеспечивающего сервис. Прозрач-­

ное управление сервисными функциями другого объекта - это основная при­

чина использования объекта-заместителя.

Объект-заместитель может использоваться для управления сервисом самыХ

разных видов. Некоторые наиболее важные описаны в этой книге как отдельные

е

при

водятся самые распространенные случаи использования

шаблоны. Ниж

 

Рroху - )1'/

Заместитель создает ВИдимость HeMeДJ1eHHOГO возврата метода, выполнение

которого требует ДJ1ительного времени.

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

(stub), и его используют RMI (Remote Method Invocation, удаленный вызов метода) , CORBA (Соmmоп Object Request Вюkеr Architecture, технология построения распределенных приложений) и другие ORB (Object Request Вюkеrs, объектный брокер запросов). Описание классов-заглушек входит

в описание ORB (в книге [Grand2001 ]).

Заместитель контролирует доступ к объекту, обеспечивающему сервис,

сточки зрения безопасности. Подобное использование заместителей опи­

сано как шаблон Рюtесtiоп Рюху (в книге [Grand2001 ]).

Заместитель создает иллюзию существования объекта, обеспечивающего

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

МОТИВЫ

©Обеспечивающий сервис объект не может предоставлять сервис в то время

ив том месте, где это удобно.

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

©Управление сервисом должно строиться так, чтобы оно было максимально прозрачным ДJ1я клиентов этого сервиса.

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

РЕШЕНИЕ

Весь доступ к сервис-объекту должен осуществляться через объект-заместитель.

Чтобы обработка обращений БЬU1а прозрачной ДJ1я клиентов, объект-замести­

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

либо реализовывать общий интерфейс (рис. 4. 1 9).

На диаграмме представлена структура шаблона Рюху, но не показаны детали,

имеющие отношение к реализации политики управления доступом. Однако

шаблон Ргоху не очень полезен, если он не реализует определенную политику

98

Глава 4. Основные шаблоны проектирования

 

 

 

 

 

Испольэует

 

«interface»

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ServiceIF

 

I

CLient

I

 

 

 

 

 

 

dolt()

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

*

 

 

 

 

 

 

 

 

I - - - - - - - - - - - -- - - - - - - - - -.

 

 

 

 

 

 

I

 

 

 

I

 

 

 

 

 

 

ServiceProxy

 

 

 

Service

 

 

 

 

 

 

doIt()

 

 

dolt()

 

 

 

 

 

 

 

 

 

 

 

 

Рис. 4.19. Классы в шаблоне Ргоху

управления доступом к сервис-объекту. Шаблон Proxy настолько широко ис­ пользуется вместе с реализацией управления доступом, что эти структуры в не­ которых книгах описываются как отдельные шаБJ.]ОНЫ.

РЕАЛИЗАЦИЯ

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

СЛЕДСТВИЯ

©Способ управления сервисом, предоставляемым сервис-объектом, прозра­ чен ДЛЯ объекта и его клиентов.

©Если использование заместителей не приводит к возникновению новых ви­ дов исключений, то, как правило, нет необходимости в том, чтобы в коде

клиентских классов учитывать использование заместителей.

ПРИМЕР КОДА

Шаблон Proxy обычно используется в сочетании с логикой управления досту­

пом к сервис-объекту. В приведенном далее примере ДЛЯ шаблона Proxy замес­

тители используются с целью отложить требующую больших затрат операцию

до тех пор, пока она действительно не понадобится. Если операция не потребу­

ется, то она никогда не будет выполняться.

Рассмотрим пример класса-заместителя j ava . util . HashMap, который реали­ зует интерфейс j ava . ut i l . Map И j ava . long . Cloneable. Задача заместителЯ состоит в том, чтобы отложить клонирование базового объекта Мар до тех пор,

пока не станет ясно, что эта требующая больших затрат операция действительно

нужна.

t'roxy • 7 7

Clone

Все классы в языке Java наследуют метод c lone от класса j ava . lang . Obj ect. Метод clone объекта возвращает поверхностную копию объек­ та. Объект, возвращаемый методом clone, является экземпляром того же самого класса, что и первоначальный объект. Все его переменные экзем­ пляра имеют те же значения, которые имеет первоначальный объект. Пе­ ременные экземпляра копии ссылаются на тот же самый объект, на кото­ рый ссьmается первоначальный объект.

В принципе, возможность копировать объект, содержащий секретную информацию, - это просчет в системе безопасности. Поэтому метод clone, унаследованный от класса Obj ect, генерирует исключение, если класс объекта не разрешает выполнять клонирование. Класс допускает клонирование своих экземпляров, если он реализует интерфейс j ava . lang . Cloneable.

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

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

то можно использовать синхронизацию для получения эксклюзивного доступа

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

ктому же объекту Мар. Подобное ожидание может быть неприемлемым.

Не каждый класс, реализующий интерфейс Мар, допускает клонирование своих

экземпляров. Тем не менее многие классы Мар, например, j ava . util . HashMap

и j ava . ut i l . TreeMap, действительно разрешают клонирование своих экземп­

ляров.

Клонирование объекта Мар перед выборкой его значени й - это защитная мера,

которая позволяет избежать необходимости синхронизации объекта Hashtable сверх того времени, которое требуется для завершения операции клонирова­

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

тывать пары « ключ - значение» из копии, и другие потоки вам не помеха.

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

первоначального объекта Мар,

то время и память, затраченные на создание кло­

на, были потеряны. Цель этого примера - показать, как избежать таких потерь.

Мар.

100 Глава 4. Основные шаблоны проектирования

Решение заключается в том, чтобы отложить клонирование объекта Мар до тех пор, пока не произойдет реальное изменение объекта.

Имя класса-заместителя - LazyCloneMap. Его экземпляры представляют со­ бой копии скопированного при записи заместителя для объекта Мар. Когда вы­ зывается метод clone заместителя, он возвращает копию заместителя, но не копирует базовый объект Мар. В этот момент как оригинал, так и копия замес­ тителя ссылаются на один и тот же базовый объект Мар. Когда один из замести­ телей получит запрос на изменение базового объекта Мар, он определит, что со­ вместно использует общий базовый объект Мар, и перед тем, как изменить его, выполнит клонирование базового объекта На рис. 4.20 представлена структура класса LazyCloneMap.

 

 

 

«;nterface»

 

 

 

 

«;nterface»

 

1

 

java.utiL.Map

java.Lang.cLoneable

 

 

 

 

 

 

 

 

 

 

 

l).

 

 

 

,

-- - - - - -

 

 

 

- - - - - - - - - - _ .

 

 

 

- - - -

Делегирует операции Мар .....

 

 

 

 

 

 

 

 

 

laZYCLoneMap

 

 

 

 

 

 

..IL -------------1 iI. cLoneO

 

 

 

 

 

 

 

 

 

 

 

Рис. 4.20. Класс-заместитель LazyCloneMap

Начало исходного текста класса LazyCloneMap выглядит так:

public class LazyCloneМap implements мар , Cloneable /**

*/Объект Мар, для которого этот объект является заместителем .

private мар underlyingМap ;

Классы-заместители дают возможность объекту-заместителю ссылаться на объект, для которого он функционирует как заместитель. Этот класс-замести­ тель имеет переменную экземпляра, которая ссылается на базовый объект Мар.

Объекты LazyCloneMap знают, когда они совместно используют общий базо­ вый объект Мар вместе с другими объектами LazyCloneMap, с помощью счет­

чика, вычисляющего количество ссылок на один и тот же базовый объект Мар. Значение счетчика содержится в экземпляре класса MutableInteger. Объект

Mutablelnteger совместно используется теми же объектами LazyCloneMap,

которые совместно используют базовый объект Мар.

Мар,

Ргоху - lUl

/* *

* Это количество объектов-заместителей,

* которые совместно используют один и тот же базовый объект . * /

private МutableInteger refCount ;

Метод c lone, который классы наследуют от класса Obj ect, является защи­ шенным 1• Не существует интерфейса, реализованного классами HashMap

и TreeMap, который объявлял бы метод clone открытым. Поэтому класс

LazyCloneMap должен получить доступ. к методу clone базовых объектов явно используя j ava . lang . reflect Method.

/ * *

* Переменная исполь зуется для вызова метода clone

*базового объекта Мар .

*/

private Method cloneMethod;

private static Сlазз [ ] cloneParams = new Сlазз [О] ;

/* *

* Constructor

*

* @pa ram unde rlyingMap

* Объект Мар, для которого этот объект должен быть

*заместителем .

*@ throws NoSuchMethodException

*Если базовьм объект Мар не содержит открытого метода

*clone .

*@throws I nvoca ti onTa rge tExcept ion

*

Объект, созданньм этим конструктором, использует клон

*данного объекта Мар . Если метод clone объекта Мар

* генерирует исключение , этот конструктор генерирует

*Invocat ionTargetExcept ion, чей метод getCause возвращает

*исключение , сгенерированное методом clone базового

*объекта Мар .

*/

public LazyCloneМap (мар underlyingмap)

throws NoSuchМethodException ,

InvocationTargetException { 1 В Java 1 .4 он уже является открытым. (Прuмеч. ред.)

102 Глава 4.

Основные шаблоны проектирования

Class mapClass = underlyingМap . qetClass () ;

cloneМethod = mapClass . qetмethod ("clone" ,cloneParams) ;

try {

 

this . underlyingМap =

 

(мар) cloneМethod. invoke (underlyingМap , null) ;

} catch (IlleqalAccessException е)

//

Этого не должно случиться .

/ /

try

refCount = new Mutablelnteqer (l) ;

/ / constructor (Map)

Этот метод clone класса не копирует базовый объект Мар. Он наращивает счетчик ссылок, который совместно используется первоначальным объектом LazyClo ­ neMap и копией. Значение счетчика (больше единицы) говорит объектам LazyCloneMap О том, что они совместно используют общий объект Мар.

public Object clone () {

 

LazyCloneмap theClone ;

 

try {

 

 

Cloneable oriqinal = (Cloneable)underlyinqмap ;

theClone

= (LazyCloneмap) super . clone () ;

catch (CloneNotSupportedException е)

(

/ / Этого

не должно быть никогда .

 

theClone

= null;

 

// t r y

 

 

refCount . setValue (l+refCount . qetValue ( »

;

return theClone;

 

// cl one ( )

 

 

Следующий закрытый метод вызывается открытыми методами класса

Laz yC l oneMap, например, put И clear, которые изменяют базовый объект

Мар. Открытые методы вызывают этот закрытый метод перед изменением ба­

зового объекта Мар.

private void ensureUnderlyingМapNotShared () if (refCount . qetValue (» l)

try { = underlyinqмap

(мар) cloneМethod . invoke (underlyingМap, null) ; refcount . =setValue (refcount . qetValue () -l) ;

refCount new Mutablelnteqer (l) ; catch (IlleqalAccessException е) {

Мар.

rroxy - ...U.;J

 

/ 1 Этого не должно случиться .

 

catch (InvocationTarqetException е) {

 

Throwable cause = e . qetCause ( ) ;

 

throw new RuntimeException ( "clone failed" ,

 

cause) ;

}

/ / try

/ /

i f

1 1 ensureUnderlyi ngMapNotShared ( )

Сначала метод ensureUnder l yi ngMapNotShared определяет, больше ли еди­ этотницы значение счетчика ссьmок. Если оно больше единицы, метод знает, что объект LazyCloneMap совместно использует свой базовый объект Мар вместе с другими объектами Laz yCloneMap. Метод ensureUnderlyingMapNotShared

клонирует базовый объект Мар, и поэтому данный объект Laz yC loneMap будет иметь свою собственную копию базового объекта Мар, которая уже не является совместно используемой. Он уменьшает счетчик ссьmок, поэтому другие объ­ екты LazyCloneMap, с которыми этот объект совместно использовал базовый объект Мар, будут знать, что они больше не разделяют один и тот же общий объект Мар с этим объектом. Кроме того, он создает новый объект, содержащий счетчик ссьmок со значением, равным 1 . Это говорит о том, что данный объект LazyC loneMap не участвует в совместном использовании базового объекта

Остальные методы класса Laz yCloneMap делегируют свои функции соответст­ вующему методу базового объекта Мар.

public int size () {

return underlyingмap . size ( ) ;

public boolean iSEmpty () {

return underlyingмap . iSEmpty ( ) ;

public boolean containsKey (Object key) { return underlyingмap . containsКey (key) ;

public boolean containsValue (Object value) { return underlyingмap . containsValue (value) ;

public Object qet (Object key) { return underlyingмap . qet (key) ;

104 Глава 4. Основные шаблоны проектирования

Следующие четыре метода изменяют базовый объект Мар, поэтому перед тем,

как делегировать свои функции базовому объекту Мар, они вызывают метод ensureUnderlyingMapNotShared.

public Object put (Objec key , Object value) { ensureUnderlyingМapNotShared ( ) ;

return underlyingМap . put (key , value) ;

public Object remove (Object key) { ensureUnderlyingМapNotShared () ; return underlyingМap . remove (key) ;

public void putAll (Мap m) { ensureUnderlyingМapNotShared () ; underlyinqMap . putAll (m) ;

public void clear () { ensureUnderlyinqМapNotShared ( ) ; underlyinqМap . clear () ;

public Set keySet ( ) {

return underlyingМap . keySet ( ) ;

public Collection values () { return underlyingМap . values ( ) ;

public Set entrySet ( ) {

return underlyinqМap . entrySet ( ) ;

public boolean equals (Object that) { return underlyingМap . equals (that) ;

public int hashCode () {

return underlyinqМap . hashCode ( ) ;

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