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

GrandM-Patterns_in_Java

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

Рroху 8 105

Листинг класса Mut ab l e lnteger, который использует класс LazyCloneMap:

pub1ic с1азз MutaЬ1eInteger pub1ic int va1 ;

pub1ic MutaЬ1eInteger ( int va1ue ) { setVa1ue ( va1ue ) ;

pub1ic int getVa1ue ( ) return ( va1 ) ;

pub1ic =void setVa1ue ( int va1ue ) { va1 va1ue ;

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

Protection Proxy. Шаблон Рюtесtiоп Рюху (описанный в книге [Grand2001 ]) использует заместителя для проведения политики безопасности при доступе к сервис-объекту.

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

Object Request Broker. Шаблон Object Request Вюkег (описанный в книге

[Grand200 1 ] ) использует заместителя для сокрытия того факта, что сервис-объ­

ект находится не на том компьютере, на котором находятся клиентские объек­

ты, которые хотят его использовать.

Virtual Proxy. Этот шаблон использует заместителя для создания иллюзии су­

ществования сервис-объекта еше до того, как он создается в действительности.

Это удобно в том случае, когда создание объекта требует больших затрат, а его

сервисы могуг не понадобиться. Заместитель, рассмотренный в разделе « При­ мер кода» для шаблона Рюху, - это разновидность виртуального заместителя.

Decorator. С точки зрения структуры шаблон Decorator аналогичен шаблону Рюху в том смысле, что он активизирует доступ к сервис-объекту, который дол­

Жен быть осушествлен косвенно через другой объект. Отличие состоит в том,

какая цель при этом преследуется. Вместо попытки управления сервисом объ­

ект, благодаря которому осушествляется косвенность, некоторым образом рас­ uшряет возможности сервиса.

Г Л А В А

Порождающие

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

Factory Method (Метод фабрики) (109)

Abstract Factory (Абстрактная фабрика) (123)

Builder (Строитель) (132)

Prototype (ПротоТ'ш) (142)

Singleton (Одиночка) (152)

Object Роо' (Пул объектов) (162)

Порождаюшие шаблоны предоставляют руководство к действию - как созда­

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

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

ответственность за создание экземпляров. Значение порождаюших шаблонов за­

ключается в том, чтобы проинформировать нас, как структурировать и инкапсу­ лировать эти решения.

Чаще всего в какой-то ситуации можно использовать несколько порождаюших

шаблонов. Иногда их выгодно комбинировать. В других случаях следует вы­ брать один из подходяших шаблонов. Поэтому важно ознакомиться со всеми

шаблонами, представленными в данной главе.

Если у вас есть время на изучение только одного шаблона данной главы, то рас­

смотрите шаблон Factory MetllOd, так как он самый распространенный. Шаб­

лон Factory Method дает возможность объекту инициировать создание другого

объекта, ничего не зная о классе этого объекта.

Шаблон Abstract Factory позволяет объектам инициировать создание объектов

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

тируя надлежашую согласованность этих классов.

Шаблон Builder разрешает определить класс создаваемого объекта по его содер-

108

Глава

5.

Парождающие шаблоны проект

ирования

 

 

 

Шаблон Prototype дает возможность объекту создавать специальные объекты,

не имея точной информации об их классе или о деталях их создания.

Шаблон Singleton позволяет многочисленным объектам совместно использо­

вать общий объект, не зная о том, существует ли он.

При помощи шаблона Object Рооl можно не создавать новые объекты, а много­

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

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

СИНОПСИС

Нужно создать объект, представляюший внешние данные, или обработать какое-то внешнее событие. Тип объекта зависит от содержимого внешних дан­

ных или от типа события. Необходимо, чтобы ни источник данных, ни источ­

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

даваемого объекта. В таком случае инкапсулируют решение о том, какой класс объекта создавать.

КОНТЕКСТ

Рассмотрим проблему написания каркаса приложений для обработки текста. Такие приложения обычно создаются для работы с документами. Их функцио­ нирование обычно начинается с команды создания или редактирования тек­

стового документа, таблицы, плана или любого документа, с которым должно работать приложение.

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

ды, - App l i cation.

Логика, лежашая в основе реализации большинства этих команд, меняется

в зависимости от типа документа, поэтому класс Appl ication обычно делеги­

рует большинство команд некоторому объекту документа. Однако сушествуют

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

ектов документа. Все

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

структуры:

 

интерфейс документа;

абстрактный класс,

который реализует обшУЮ для конкретных классов до­

кументов логику;

 

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

терфейс для документов специального типа.

Подобная структура представлена на рис. 5 . 1 .

10 Глава 5. Порождающие шаблоны проектирования

«interface»

 

 

 

DocumentIF

 

 

....

getТitLe

*

Редактирует

 

newDocument

1

openDocument

 

 

 

. . .

 

 

 

AbstradDocuтent

getТitLe

. ..

 

 

MyDocument

newDocument openDocument

. . .

AppLication

newDocument openDocument

.. .

Рис. 5.1. Каркас приложения

lи на рис. 5. 1 , ни в ходе предыдушеro рассмотрения не было показано, как не­ lВИСИМЫЙ от различных типов документов объект Appl i ca t i on создает эк­

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

Iрограммист может решить данную проблему посредством использования

аркаса для создания класса, инкапсулируюшего логику, предназначенную для

ыбора и инстанциирования зависяших от типа документов классов. Чтобы ласс App l i cation мог вызывать созданный программистом класс, будучи не­ lВИСИМЫМ от него, каркас приложения должен предоставлять интерфейс, ко­ орый имплементирует созданный программистом класс. Такой интерфейс бъявлял бы метод, предназначенный для выбора и инстанциирования класса.

асс App l i ca t i on будет работать через интерфейс, предоставляемый карка­

ом приложения, а не с классом, предоставляемым программистом. На рис. 5.2

оказана такая структура.

>бъект App l i cation вызывает метод createDocument объекта, реализующего

нтерфейс DocumentFactoryI F. Он передает строку методу createDocument,

оторая сообщает, какой подкласс класса Document инстанциировать. Классу ppl ication не нужно знать о реальном классе объекта, метод которого он

ызывает, или о том, какие подклассы класса Document он инстанциирует.

 

 

 

 

 

 

 

 

 

 

 

Fadory Method

1 1 1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

«interface»

 

 

 

 

 

 

 

AppLication

 

 

 

 

 

 

DocumentIF

Редактирует

 

 

1

 

newDocument

 

 

 

 

 

 

 

 

 

 

 

 

getTitLe

 

 

 

openDocument

 

 

newDocument

*

 

 

 

 

. . .

 

инициатор

 

 

openDocument

 

 

 

 

 

 

 

 

. . ,

 

 

 

 

<111Запрашивает создание1 1

 

запроса

 

 

 

 

 

 

 

 

 

 

I

 

 

1

создатель

 

 

 

 

 

 

 

I

 

1

 

 

 

 

 

 

 

 

 

 

 

 

I

 

 

 

 

 

 

 

 

 

 

 

 

 

I

 

 

«interface»

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

I

 

 

DocumentFactoryIF

 

 

 

 

 

 

 

II

 

 

 

 

 

AbstradDocument

 

 

 

 

 

 

 

 

 

 

 

 

createDocument(type:String):DocumentIF

 

 

9

 

 

 

 

 

 

etТitLe

 

 

1)I.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

I

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

I

 

 

 

 

 

 

 

 

 

 

 

 

MyDocumentri

 

 

 

I

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

I

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

DocumentFactory

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

createDocument(type:String):DocumentIF

 

 

 

 

 

 

 

 

 

 

1

 

 

 

 

 

 

 

 

 

 

 

<111Создает

 

 

 

 

 

 

 

 

 

openDocument

 

 

 

 

 

 

 

 

 

newDocument

*

 

 

 

 

 

 

 

 

 

 

. .

.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Рис. 5.2. Каркас nриложения вместе с классом-фабрикой документов

МОТИВЫ

©Класс должен иметь возможность инициировать создание объектов, не бу­ дучи каким-либо образом зависимым от класса создаваемого объекта.

©Считается, что класс может инстанциировать набор классов, который мо­ жет быть динамичным и изменяться по мере того, как становятся доступ­

ными новые классы.

РЕШЕНИЕ

Шаблон Factory Method представляет собой независимые от приложения объ­

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

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

На рис. 5.3 показаны интерфейсы и классы, которые обычно образуют шаблон

Factory Method.

1 12 Глава 5.

Параждающие шаблоны проектирования

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1

CreationRequester

 

 

 

 

 

 

 

 

 

 

 

 

 

newProduct

 

 

 

 

 

 

Использует

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

. . .

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1

инициатор

 

 

 

 

 

 

 

 

 

 

 

Запрашивает создание 1

запроса

 

 

 

 

 

 

 

 

создатель

 

*

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1

 

«interface»

 

 

 

 

,[1

 

 

 

 

 

 

 

 

FactoryIF

 

 

 

 

1

 

 

 

 

createProduct(discriminator):ProductIF

 

 

 

 

 

 

 

 

 

 

А.

 

 

 

 

«interface»

 

Создает

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

I

 

 

 

 

DocumentIF

 

 

 

 

 

 

 

I

 

 

 

 

 

 

 

 

 

 

I

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

I

 

 

 

 

operation1

 

 

*

 

 

 

1

1

 

 

 

 

 

 

 

 

 

 

I

 

 

 

 

operation2

 

 

 

 

 

 

 

 

 

Factory

 

 

 

...

 

 

 

 

 

 

createProduct(discriminator):ProductIF

 

 

 

 

 

 

 

 

 

 

 

I

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

I

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

I

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

I

 

 

 

I

 

 

 

 

 

:

 

 

 

IrI _ _ _ _ _ _ _ _ _ IL _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ,I

_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ I

 

 

 

ConcreteProduct1

 

 

 

 

 

ConcreteProduct2

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

operation1

 

 

 

 

 

operation1

 

 

 

 

 

 

 

 

 

operation2

 

 

 

 

 

operation2

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Рис. 5.3. Шаблон Factory Method

Рассмотрим роли, которые играют эти классы и интерфейсы.

ProductlF. Объекты, созданные с помощью шаблона Factory Method, должн' реализовывать интерфейс, выступающийи Т.Д. в этой роли.

Concrete ProductI, Concrete Product2 Классы, играющие эти роли, инста,.

циируются объектом Factory. Эги классы должны реализовывать интерфеJII

Product I F.

Creation Requester. Класс, выступающий в этой роли, не зависит от приложен и нужен для создания классов, зависящих от приложений. Он делает это ко венно, при помощи экземпляра класса, реализующего интерфейс FactoryI

Factory IF. Это не зависящий от приложения интерфейс. Объекты, котор создают объекты ProductI F на правах объектов CreationRequester, должн реализовывать этот интерфейс. Интерфейсы такого рода объявляют метод, к торый вызывается объектом CreationRequester для создания объектов кон кретных продуктов. Аргументы, которые принимает этот метод, рассматрива

ются в разделе «Реализация» в описании данного шаблона.

Factory Method - 1 13

Выполняющие эту роль интерфейсы обычно носят имя, которое включает слово

«Factory», например, DocumentFactoryIF или ImageFactory I F.

Factory. это зависящий от приложения класс, который реализует соответствующий интерфейс FactoryIF и имеет метод для создания объектов ConcreteProduct. Выполняющие эту роль классы обычно имеют имена, включающие слово

(,Factory.) , например, DocumentFactory или ImageFactory.

РЕАЛИЗАЦИЯ

Во многих реализациях шаблона Factory Method классы ConcreteProduct не

прямым образом реализуют интерфейс Product I F. Вместо этого они наследу­ ются от абстрактного класса, который реализует этот интерфейс. Причины та­ кого поведения рассматриваются при описании шаблона Inteгface and Abstract

Class (см. гл. 4).

О п р ед е л е н и е кл а сс а п о к о н ф и г ур а ц и и

Существуют два основных варианта шаблона Factory Method. В общем случае класс создаваемого объекта определяется на стадии создания этого объекта. Менее общий случай предполагает, что класс создаваемого объекта всегда один

и тот же и определяется до начала создания объекта.

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

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

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

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

компании выделен соответствующий класс, который реализует нужный интер­

фейс и знает, как отправлять сообщения, ожидаемые компанией. Имеется так­

же соответствующий класс-фабрика. Подобная структура показана на рис. 5.4.

Когда начинает работать система терминалов, она считывает соответствующую ИНформацию по конфигурации, где сообщается, что она должна передать обра­ ботку операции по кредитной карте либо BancOneCC, либо We llsCCFargo.

На основании этой информации система торговых терминалов создает либо

объект BancOneCCFactory, либо Wel lsCCFactory и получает к нему доступ

через интерфейс Credi tCardProcessorFactoryIF. Когда объект должен обра­ батывать операцию с кредитной картой, он вызывает метод createProcessor Своего интерфейса Credi tCardProcessorFactoryIF, который и создает объ­ ект, использующий заданную в конфигурации процедуру обработки кредитной

карты. Обратите внимание, что метод Create класса-фабрики не нуждается в каких-либо аргументах, поскольку он всегда возвращает объект одного и того

же типа.

1 1.4 Глава 5.

 

Порождающие шаблоны проектирования

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

I PointOfSaLe

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

...Использует

 

 

 

 

1 1

инициатор

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

...Запраwивает создание

 

запроса

 

 

 

 

 

, 1

создатель 1 .

«interface»

 

 

 

 

 

 

 

 

CreditCardProcessorFactoryIF

 

 

 

 

 

«interface»

 

createProcessorO :CreditCardProcessorIF

 

 

CreditCardProcessorIF

 

 

 

 

11

 

 

 

 

 

processCharge

 

 

 

 

 

 

 

I

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

I

 

 

 

 

processCredit

 

 

 

 

 

 

 

 

 

 

 

I

 

 

 

 

 

 

 

 

 

' - - - - - - - --- - - - - - - - - - ,

 

.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

I

 

I

 

 

 

. .

 

 

 

 

 

 

 

 

 

 

 

 

 

 

I

 

II

 

 

 

 

 

t;I..

 

 

 

BancOneCCFactory

 

 

( - - - - - - - - --1- - - - - - ---- '

 

 

 

 

createProcessorO :CreditCardProcessorIF

 

 

 

I

 

 

I

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

I

 

 

 

 

 

 

 

 

 

 

 

I

 

 

 

 

 

 

I

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

WeLLsCC

 

 

 

ВапсОпеСС

 

 

 

"' Создает

 

 

 

 

 

 

 

 

оретаооп1

 

 

 

 

 

operation1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

оретаооп2

 

 

 

 

 

operation2

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

"' Создает

 

 

 

 

WeLLsCCFactory

 

 

 

 

 

 

 

 

 

 

 

createProcessorO :CreditCardProcessorIF

 

'-------------'----'------1

 

 

 

 

 

 

 

 

Рис. 5..4. Обработка кредитной карты

 

 

О п р ед ел е н и е кл а сс а на о с н о в е да н н ы х

Очень часто класс объекта, создаваемого объектом-фабрикой, определяется нз,1 основании данных, которые должны быть в нем инкапсулированы. Класс опре-i деляется методом createProduct объекта-фабрики. Определение обычно ос­ новывается на информации, переданной методу в виде параметра. Метод createProduct часто имеет следующий вид:

Imaqe createImaqe (Strinq ext)

if

(ext . equals ("qif")

 

return new GIFImaqe () ;

if

(ext . equals ("jpeq" »

 

return new JPEGImaqe () ;

// crea telmage ( S t ring)

Такой набор команд if хорошо работает для тех методов createProduct, кото­ рые имеют постоянный набор классов продуктов, ПОдЛежащих инстанциированию.

Foctory Method 8 115

Чтобы написать метод c reateProduct, обрабатывающий переменное или большое число классов продуктов, можно использовать шаблон Hashed Adapter Object (описанный в книге [Grand99]). В качестве альтернативного варианта можно использовать различные объекты, которые указывают на инстанциируе­ мый класс, являясь ключами в хэш-таблице, значения в которой задаются объ­ ектами j ava . l ang . reflect . Constructor. При меняя этот способ, ищут зна­

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

Предыдущий пример иллюстрирует то, что методы объекта-фабрикиi - ПОдХо­ дящее место для размещения команд swi tch или цепочки команд f. Во мно­ rих случаях наличие в коде команд swi t ch или цепочек команд i f указывает на то, что метод должен быть реализован как полиморфный. Методы объекта-фабрики, однако, не могут быть реализованы при помощи полимор­ физма, поскольку полиморфизм работает только после того, как объект уже был создан.

Для многих случаев реализации шаблона Factory Method аргументы для метода createProduct объекта-фабрики MOIyr быть представлены в виде набора заранее определенных значений. Классу-фабрике часто удобно задавать символические

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

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

СЛЕДСТВИЯ

©Запрашивающий создание класс не зависит от реально создаваемых классов объектов конкретных продуктов.

©Набор инстанциируемых классов продуктов может изменяться динамиче­ ски.

®Косвенность, существующая между инициированием создания объекта и определением инстанциируемого класса, может усложнить понимание

программы специалистами из команды поддержки.

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

для интеграции оболочки апплета и основной программы в некоторых местах Java API используется шаблон Factory Method. Например, каждый объект URL Имеет связанный С ним объект URLConnection. Можно использовать объекты URLConnection для чтения исходных байтов URL. Кроме того, объекты

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