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

GrandM-Patterns_in_Java

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

136

 

Глава 5.

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

 

 

 

 

 

 

 

 

 

 

1

 

 

 

 

 

Использует

 

 

 

 

 

 

 

 

 

 

 

 

 

 

I

1

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

 

 

 

 

 

 

 

 

 

 

 

 

Cl ent

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

i

I инициатор

 

 

создатель

l 1. .*

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

запроса

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1

инициатор

 

 

 

 

 

 

 

 

 

 

 

0. .

*

 

 

 

 

 

Abstract8uilder

 

 

 

 

 

 

 

 

 

запроса

 

 

 

 

getInstance():AbstractByilder

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

.....Запрашивает направление

1

 

 

 

 

 

«interface»

 

 

 

 

 

 

 

 

buildPartl ()

 

 

 

 

 

ProductIF

 

строительства

 

 

 

... buildPart2().

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

*

 

 

1

управляющий

 

Направляет ' getProduct():ProductIF. .

 

 

 

 

 

 

 

 

 

 

 

 

 

1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

I

 

Director

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Build(:Builder):ProductIF

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ConcreteBuilder

1

Создае

 

1 .1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

buildPart1()

 

 

т

 

 

Product

 

 

 

 

 

 

 

 

 

 

buildPart2()

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

.. .

 

 

 

 

 

"1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

getProduct():ProductIF

 

 

 

 

 

 

 

 

 

 

 

 

Рис. 5.11. Шаблон Builder

ProductIF. Шаблон Builder используется для создания объектов класса Produc1i самых разных видов, эти объекты используются объектами класса Cl ient. Что­ бы объекты класса C l i ent не нУЖДалисьв информации о реальном классе объi ектов Product, созданных для них, все классы Product реализуют интерфей6

ProductI F. Объекты Cli ent ссьшаются на созданные для них объекты Product

r

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

Client. Экземпляр клиентского класса инициирует действия шаблона Builder. . Он вызывает метод get Instance класса Abs tractBuilder. Передавая

мацию методу get I ns tance, он тем самым сообщает ему, продукт какого в ..

должен быть создан. Метод

get Instance

определяет создаваемый

подкла

 

 

ИНФ

класса Abst ractBui lder

и

возвращает его объекту C l ient. Затем

объе

C lient передает объект,

полученный от getInstance, методу bu i l d класс4

Director, который создает нужный объект.

 

,

 

 

ConcreteBuilder. Класс в этой роли представляет собой конкретный подклас1

класса Abs tractBui l der, который используется для создания определенноr<JI

вида представления данных объектом Di rector.

1

AbstractBuilder. Класс в этой роли - это абстрактный суперкласс классо

ConcreteBu ilder. Класс Abs tractBu i l der имеет статический метод, обычнoi

с именем getInstance, которому передается некий аргумент, задающий тит представления данных. Метод getInstance возвращает экземпляр классакон-: кретного строителя, который создает заданное представление данных.

Builder 1 37

ньКроме! того, класс Abs tractBui lder определяет методы, которые представле­ на диаграмме классов как bui ldPartl, bui ldPart2 и т.д. Эти методы вы­ зываются объектом Di rector, чтобы сообщить объекту, возвращаемому мето­

дом getInstance, какое содержимое нужно поместить в созданный объект.

И наконец, класс-строитель определяет метод, обычно с именем getProduct,

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

телем.

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

чтобы сконструировать объект продукта.

На рис. 5 . 1 2 представлена диаграмма взаимодействия, демонстрирующая, как

эти классы работают вместе.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1: builder :- getInstance

 

 

 

 

I

 

 

 

 

 

 

 

I

AbstractBuilder

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

I

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

I

 

 

 

 

 

 

II

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

3: product :- getProduct()

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2.1: buildPart1()

 

 

 

 

 

 

1

 

__

 

 

 

 

 

 

 

 

 

I

 

 

 

 

:t------------i:

 

 

 

 

 

 

 

 

 

 

 

 

2.2: buildPart2()

 

 

 

 

 

 

 

...

:Oirector

....

 

 

 

 

 

 

builder:ConcreteBuilder

 

 

__

___

 

 

 

 

 

 

 

 

 

 

 

__

 

 

 

 

 

 

 

 

 

 

 

 

Рис. 5.12. Взаимодействие в шаблоне Builder

РЕАЛИЗАЦИЯ

в результате проектирования и реализации шаблона Builder получается набор

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

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

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

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

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

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

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

СЛЕДСТВИЯ

©Задание содержимого и построение определенного представления данных

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

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

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

не нуждаясь при этом в каких-либо изменениях.

Шаблон Builder обеспечивает более точный контроль во время построения,

чем другие шаблоны (например, Factory Method), давая возможность объек­

ту-распорядителю шаг за шагом контролировать весь процесс создания объекта-продукта. Другие шаблоны просто создают сразу весь объект.

Рассмотрим образец кода классов этого примера, взаимодействующих в ра

ПРИМЕР КОДА для м­

ках шаблона Builder. Экземпляры класса MIMEParser играют роль объек­

тов-распорядителей. Приведем исходный текст для класса MIMEParser:

class MIМEParser (

private MIМEМessage msg;

private MessageBuilder bUilder;

// Синтаксически

//анализируемое сообщение .

// Объект-строитель .

Builder 139

/* *

* Синтаксический анализ МIМЕ-сообщения, в ходе которого

*вызываются методы строителей, соответствующие полям

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

*/

OutboundМessaqeIF parse () (

builder = MessaqeВuilder . qetInstance (qetDestination ( » ;

MessaqePart hdr = nextнeader () ;

while

(hdr != null) (

if

(hdr . qetName () . equals ("to"»

builder . to « Strinq) hdr .qetValue (» ; else if (hdr . qetNaтe () . equals ("from") )

 

builder . from «

Strinq) hdr . qetValue (» ;

hdr = nextнeader () ;

//

while hdr

 

МessaqePart bdy = nextвodyPart () ;

while

(bdy ! = null)

(

Strinq пате = bdy . qetName () ;

if (name . equalsIqnoreCase ("text/plain" »

 

 

builder . plainText « strinq) bdy . qetValue (» ;

 

 

else if (name . equalsIqnoreCase ("imaqe/jpeq"»

 

 

builder . jpeqImaqe «

Imaqe)bdy . qetvalue (» ;

 

 

bdy = nextнeader () ;

 

 

 

 

// while bdy

 

 

 

return builder . qetOutboundМsq () ;

 

//

parse (Message)

 

 

private class мessaqePart

 

 

 

private Strinq name ;

 

 

 

private Object value ;

 

 

 

МessaqePart (Strinq пате , Object value) {

 

 

this

пате пате ;

 

 

 

 

this

.. value== value ;

 

 

 

} // Cons t ructor ( String, String)

)

 

Strinq qetNaтe ()

(

return пате ; )

 

Object qetValue ()

(

return value ;

//

class Mes sagePart

 

 

//

class MIMEParser

 

 

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

Цепочки команд i f, содержащиеся в методе parse показанного выше класса,

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

дарт MIME поддерживает свыше 25 видов только полей заголовков. Менее тру­

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

Hashed Adapter Objects (описанного в книге [Gгапd99]).

Приведем код для класса MessageBu ilder, который играет роль абстрактного класса-строителя:

abstract class MessageBuilder {

/ * *

*Возвращает экземпляр подкласса, соответствующий формату

*сообщения электронной почты . Формат выбирается,

*исходя из данного адреса назначения .

* @param dest Адрес электронной почты, по которому должно

*быть отправлено сообщение .

*/

static МessageBuilder getInstance (String dest) { МessageBuilder builder = null ;

return builder;

// get lns tance ( S tring)

/* *

* Установить значение поля заголовка " to" . * /

abstract void to (String value) ;

/ * *

*Установить значение поля заголовка " from" .

*/

abstract void from(String value) ;

/ * *

* Установить значение поля заголовка " o rgani zat ion" .

* /

void organization (String value) { )

/* *

* ДОбавить текстовое сообщение .

* /

abstract void plainText (String content) ;

Builder 141

/ * * * Завершить и возвратить отправляемое сообщение электронной

*почты .

*/

abstract OutboundМessageIF getOutboundМsg ()

/ / class Mes s ageBu i lder

и наконец, так выглядит код ДЛЯ интерфейса OutboundMe s sage I F:

public interface OutboundМessageIF } publicinterfacevoid sendOutbound() ; МsgIF

//

ШАБЛОНbI ПРОЕКТИРОВАНИЯ,

СВЯЗАННblЕ С ШАБЛОНОМ BUILDER

Inteгface. Шаблон Builder использует шаблон Interface ДЛЯ сокрытия класса объекта Product I F.

Composite. Объект, созданный при помощи шаблона Builder, обычно является составным.

Factory Method. Шаблон Builder использует шаблон Factory Method ДЛЯ при­ нятия решения, какой класс конкретного строителя инстанциировать.

Template Method. При реализации класса Abstract Builder часто используется шаблон Template Method.

Null Object. Шаблон NиН Object может использоваться шаблоном Builder для Получения данных из бездействующих реализаций методов.

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

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

СИНОПСИС не

Шаблон Prototype позволяет создавать специальные объекты, ничего точно зная об их классе или деталях их создания. Механизм можно описать так: объ­ екту, инициирующему создание других объектов, предоставляются объек­ ты-прототипы. Затем этот объект создает объекты путем копирования этих объектов-прототипов. I

КОНТЕКСТ

Предположим, что создается программа CAD (Computer-AssistedDesign, система автоматизированного проектирования), которая позволит пользователям чеPr титьдиаграммы при помощи палитры символов. Программа будляет иметь основ­ ной набор встроенных символовдля . Однако ее могут применять выполнен", разных нестандартныхзадач, решения которых не подойдет основной набоЪ символов. Поэтому должна существовать возможность предоставления допоJ1t нительных наборов символов, которые пользователи смогут при необходимости добавлять в программу.

При этом возникает проблема, каким образом предоставить эти палитры дct полнительных символов. Можно без труда организовать программу таким общ>; разом, чтобы все символы (и основные, и дополнительные) передавались наследствуот бщего родительского класса. При этом остальная часть програrt мы, рисующеи диаграммы, получает возможность согласованного управлени)I объектами символов. Но остается открытым вопрос, как программа будет созr давать эти объекты. Создание объектов подобного рода зачастую сложнее пРОус!'­ стого инстанциирования класса. Кроме того, при этом может потребоваться тановка значений для атрибутов данных или комбинация объектов с цель., создания составного объекта.

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

используемых в качестве прототипов, заключается в том, что они должнЫ

иметь метод, обычно с именем clone, который возвращает новый объект, яв­f

ляющийся копией исходного объекта (рис. 5. 1 3).

Программа содержит коллекцию объектов-прототипов Symbol. Она использу объекты Symbol , клонируя их. Объекты SymbolBuilder создают объект

Symbol и регистрируют их в программе рисования.

Prototype 143

SymbolBuilder

* создатеnь

Создает и регистрирует символы

 

 

 

 

1

1

. . *

Symbol

 

Рис. 5.13. Прототип символа

Все Jаvа-классы наследуют от класса Object метод clone, который возвращает копию объекта. Клонирование производится только для экземпляров тех клас­ сов, которые дают на это разрешение. Класс дает разрешение на клонирование своего экземпляра в том и только в том случае, когда он реализует интерфейс Cloneable. Класс Syrnbol реализует интерфейс Cloneable, поэтому програм­ ма рисования может клонировать объекты Symbol, которыми она упраRЛЯет, и может включать такие объекты в рисунки.

моти в ы

©Система должна иметь возможность создавать объекты, ничего точно не

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

©Инстанциируемые классы не известны системе вплоть до стадии выполне­ ния, когда они запрашиваются «на ходу') некоторыми процессами, напри­ мер, динамической компоновкой.

©Следующие подходы, допускающие создание большого количества разных объектов, являются нежелательными:

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

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

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

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

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

РЕШЕНИЕ

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

На рис. 5. 14 представлена структура шаблона Prototype. Рассмотрим роли, ко­ торые играют эти классы и интерфейс.

ель

 

СОЗАат

 

СОЗАает и регистрирует объекты-прототипы

регистратор

1

 

CLiепt

 

 

registerPrototype

 

 

 

 

Использует

 

1

1

. .

*

 

 

Prototype

Рис. 5.14. Шаблон Prototype

Client. Класс Cl ient представляет часть программы, для которой и предназна­

чен шаблон Prototype. Класс Cl ient должен создавать объекты, о которых он

знает немного. Он будет содержать метод, который может вызываться с целью

добавления объекта-прототипа в коллекцию объектов класса Cl ient. На рис. 5.14

этот метод называется registerPrototype. Однако для практической реали­

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

типом, например, regis terSymbol .

Prototype. Классы в этой роли реализуют интерфейс PrototypeI F и инстан­

циируются С целью их клонирования клиентом. Классы, выступающие в этой

роли, обычно являются абстрактными классами, имеющими определенное ко­

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

PrototypeIF. Все объекты-прототипы должны реализовывать интерфейс, вы­

ступающий в этой роли. Класс клиента взаимодействует с объектами-прототи­

пами через этот интерфейс. Интерфейсы, играющие эту роль, должны импле­

ментировать интерфейс C loneable. Таким образом, все объекты, реализующие интерфейс Prototype I F, могут быть клонированы.

PrototypeBuilder. Он соответствует любому классу, который инстанциируется­ с целью предоставления объектов-прототиповдля объекта C l ient. Такие классы

Prototype 145

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

например, S y r n bolBui lder.

Объект PrototypeBui l der создает объекты Prototype. Он передает каждый вновь созданный объект Prototype методу registerPrototype объектаCl ient.

РЕАЛИЗАЦИЯ

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

Если это является проблемой, то PrototypeBui lder могут быть избавлены от информации о конкретном классе клиентских объектов посредством введения в систему интерфейса или абстрактного класса, реализовывать или наследовать которые должны будут клиентские классы.

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

вания объектов-прототипов. Существуют два основных подхода к реализации операции клонирования:

1 . Поверхностное копирование означает, что переменные клонированного объ­ екта содержат те же значения, что и переменные исходного объекта, и что все ссылки указывают на одинаковые объекты. Другими словами, при поверхност­ ном копировании копируется только клонируемый объект, но не объекты, на которые он ссылается. И оригинал, и поверхностная копия ссылаются на одни

и те же объекты.

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

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

глубоком копировании копируется клонируемый объект и те объекты, на кото­ Рые он ссылается. Глубокая копия ссылается на копии тех объектов, на кото­ Рые ссылается исходный объект.

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

Принимать рещение, делать глубокие или поверхностные копии косвенно ко­

Пируемых объектов. Кроме того, необходимо очень осторожно обращаться с любыми циклическими ССЬUIками.

Поверхностное копирование реализуется проще, так как все классы наследуют

Метод clone класса Obj ect, который легко это делает. Однако если класс объ­ екта не реализует интерфейс Cloneable, то метод c l one не будет работать.

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

сами себя по методу поверхностного копирования, то, объявив интерфейс

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