GrandM-Patterns_in_Java
.pdfР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. Кроме того, объекты