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

GrandM-Patterns_in_Java

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

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

PrototypeI F как расширение интерфейса C loneable, можно будет сэконо­ мить время. Таким образом, все классы, реализующие интерфейс PrototypeI F,

будут реализовывать также интерфейс Cloneable.

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

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

ванных объектов придется создавать эквивалентные объекты.

Если в палитре объекта C li ent, состоящей из объектов-прототипов, количест­

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

для ссылки на каждый объект-прототип. Проще использовать объект коллек­

ции, который может содержать динамически расширяющуюся или сужающую­

ся палитру, состоящую из объектов-прототипов. Объект коллекции, исполняю­

ший эту роль в шаблоне Prototype, называется управляющим прототипом.

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

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

СЛЕДСТВИЯ

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

предложить ни один другой порождаюший шаблон проектирования, опи­ санный в данной книге.

©Объект PrototypeBui lder может просто предоставлять постоянный набор

объектов-прототипов.

©Объект PrototypeBui lder может обеспечивать дополнительную гибкость,

допуская создание новых объектов-прототипов посредством комбинации

объектов или изменения значений атрибутов объектов.

©Клиентский объект тоже можетсоздавать объекты-прототипы новых видов.

В примере программы рисования, рассмотренном ранее, клиентский объ­

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

и затем вставлять его в свою палитру.

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

подробности создания объектов-прототипов.

© Объекты PrototypeBui lder инкапсулируют детали построения объек­

тов-прототипов.

Pгototype 147

©Если объекты-прототипы будут реализовывать интерфейс, например, Pro­ totype I F, то шаблон Prototype гарантирует предоставление объектами-про­ тотипами согласованного набора методов, используемого клиентскими

объектами.

Нет необходимости в том, чтобы объекты-прототипы были представлены в виде какой-либо иерархии классов.

® Недостатком шаблона Prototype является дополнительное время, затрачи­

ваемое на написание классов PrototypeBui lder.

® Программы, использующие шаблон Prototype, зависят от динамической

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

ПРИМЕНЕНИЕ В JAVA APIдля

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

ПРИМЕР КОДА

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

ми и теми же действующими лицами, и они захотят поиграть с новыми

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

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

Используемые в игре действующие лица - это экземпляры относительно не­

большого количества классов, например, Hero, Foo l , Vi l l ian и Monster.

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

ся в различии задаваемых для них значений атрибутов, например, изображе­

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

На рис. 5. 1 5 показаны некоторые классы, используемые в игре.

етНиже представлен код для интерфейса Character I F. Этот интерфейс выступа­

в роли PrototypeI F.

public inter ace CharacterIF extends cloneaыle { public String getName () ;

148

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

 

 

;

 

 

 

public void setNaтe (String пате )

;

 

 

public Image getImage ( )

 

 

 

 

 

 

 

public void setImage (Image image)

 

 

 

public int getStrength () ;

 

 

 

public void setStrength (int strength)

}

// class CharacterI F

 

 

 

 

 

 

 

 

 

 

 

 

((interface»

 

 

 

 

CLoneable

 

 

 

 

 

 

getRandomCharacter

 

.......

 

 

 

 

 

 

 

CharacterManager

 

Использует

 

 

addCharacter...

 

 

 

 

 

 

 

1

 

1..

 

регистратор

1

 

 

 

 

 

 

 

 

 

 

 

Создает и регистрирует

 

 

 

 

 

 

объекты

 

 

 

 

 

создатель

0..*

 

 

 

 

 

 

CharacterLoader

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Hero

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

getBravery

 

 

 

 

 

 

 

setBravery

 

 

 

 

 

 

 

 

«interfoce» ChorocterIF

getImage.. setImage

. I

I

I

Chorocter

getImage.. setImage

.

f

Monster

getViciousness setViciousness

Рис. 5.15. Использование шаблона Prototype

А теперь приведем исходный текст ДIlЯ класса Character - абстрактного клас­

са, выступающего в роли Prototype:

public abstract class Character implements CharacterIF {

/**Замещаем clone - делаем его открытым .

*

*/

public Object clone () { try (

return super . clone () ;

Pгototype 149

catch (CloneNotSupportedException е) { // Этого не должно случиться никогда,

// так как этот класс реализует Cloneable . throwtry new InternalError () ;

//

/ / clone ( )

public Strinq qetNaтe ( )

{ return пате ; }

 

public

void

setNaтe (Strinq пате) {

this . name

пате ; }

public

Imaqe

qetImaqe ( )

{ return imaqe ; }

= imaqe ;

public void

set Imaqe (Imaqe imaqe)

{ this . imaqe

}// class Character

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

Приведем исходный текст программы для класса Hero. Это один из классов,

который выступает в роли прототипа:

public class Hero extends Character private int bravery;

public int qetBravery ( ) { return bravery;

public void setBravery= (int bravery) { this . bravery bravery;

}

// class Hero

Класс Monster похож на класс Hero.

Приведем код для класса CharacterManager, играющего роль клиентского l<Ласса:

public class CharacterМanaqer= {

private Vector characters new Vector () ;

/*** Constructor @pa ram сm

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

/** * Возвращаем копию случайно выбранного объекта

* персонажа из коллекции .

* /

Character= getRandomCharacter ()

int i (int) (characters . size () *Мath . random(» ; Character с = (Character) characters . elementAt (i) ; return (Character) c . clone () ;

// getRandomCharacter ( )

/**

*Добавляем объект-прототип в коллекцию .

*/

void addcharacter (Character character) characters . addElement (character) ;

}// addCharacter ( Character)

}// class CharacterManager

Приведем коддля класса CharacterLoader, играющего роль PrototypeBuil­ der:

/**

*Этот класс загружает объекты действующих лиц

*и добавляет их в CharacterManager .

*/

class CharacterLoader (

private CharacterКanager mgr;

* CharacterManager, с KOTOPЬ будет работать этот объект .

* /

= ст)

CharacterLoader (CharacterМanager mgr ст;

} // Constructor (CharacterManager)

/ * *

*

Загружаем объекты действующих лиц из заданного файла .

*

Так как сбой при загрузке влияет только на остальную часть

*

про граммы,

не позволяя добавить в игру новые объекты

*

персонажей,

нам не нужно генерировать какие-либо

*

исключения .

 

*/

 

 

Prototype 151

int loadCharacters (String= fname) {

int objectCount О ; // Количество загруженных объектов .

// Если создание InputStream заканчивается неудачно ,

//просто выходим из метода .

try

Inputstream in;

in

=

new FileInputStream(fname) ;

in

=

new BufferedInputStream (in) ;

ObjectInputStream oIn = new ObjectInputStream(in) ;

while (true)

{

 

Object с

= oIn . readObject () ;

 

if

(с instanceof Character) {

 

 

mgr . addCharacter « Character) c) ;

 

}

/ / i f

 

}/ / while

}catch (Exception е) { // try

return objectCount;

//loadCha racters ( String)

}// class Characte rLoader

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

с ШАБЛОНОМ PROTOТYPE

Composite. Шаблон Prototype часто используется вместе с шаблоном Composite.

Композиция применяется для организации объектов-прототипов.

Abstract Factory. Шаблон Abstract Factory может быть хорошей альтернативой шаблону Prototype в тех случаях, когда не нужны допускаемые шаблоном

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

Классы PrototypeBui lder могут использовать шаблон Abstract Factory для

создания набора объектов-прототипов.

Facade. Клиентский класс обычно действует как класс-фасад, отделяющий

другие классы,участвующие в шаблоне Prototype, от остальной части проrpаммы.

Factory Method. Шаблон Factory Method может быть альтернативой шаблону

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

ГДа содержит не более одного объекта.

Decorator. Шаблон Prototype часто используется вместе с шаблоном Decorator

дЛя составления объектов-прототипов.

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

СИНОПСИС

гоШаблон Singleton гарантирует, что создается только один экземпляр некоторо­ класса. Все объекты, использующие экземпляр этого класса, имеют дел

с одним и тем же экземпляром.

КОНТЕКСТ

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

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

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

Чтобы предотвратить нежелательную ситуацию проигрывания двух аудиокл пов в одно И то же время, создаваемый класс должен прервать проигрывани аудиоклипа до того, как начнет воспроизводиться следующий. Способ проекз,!" тирования класса для осуществления такой политики, в то же время сохраня этот класс простым, заключается в том, что должно гарантироваться наличиобъ": только одного экземпляра такого класса, совместно используемого всеми ектами, применяющими этот класс. Если все запросы на проигрывание ауди - клипов идут через один и тот же объект, то этот объект может просто npepвaTI!t какой-то аудиоклип перед началом воспроизведения следующего. На рис. 5. 1' представлен такой класс.

Конструктор класса AudioCl ipManager является закрытым. Это запрещает другому классу прямым образом создавать экземпляр класса AudioCl ip!vlanager. Вместо этого для получения экземпляра класса AudioCl ipManager другие классы должны вызывать его метод g e t l n s tance. Это статический метод,

который всегда возвращает один и тот же экземпляр класса AudioCl ipManager.

Singleton 153

AudioCLipManager

-instаnсе:АudiоCliI!Маnаgеr -рrеvCLiр:АudiоCliр

«constructor» ) -АudiоCliрМаnаgеr( «misc»

+getInstance( ):AudioCliI!Manager +pLay(:AudioCliр) +loop(:AudioClip)

+stop( )

. . .

Рис. 5.16. Класс, управляющий аудиокnилом

Возвращаемый им экземпляр - это тот экземпляр, на который ссылается его закрытая статическая переменная instance.

Остальные методы класса AudioClipManager отвечают за контроль над вос­ произведением аудиоклипов. Класс AudioClipManager имеет закрытую по­ стояннуюв переменную экземпляра prevClip, которая вначале установлена null, а позднее указывает на последний проигрывавшийся аудиоклип. Перед воспроизведением нового аудиоклипа экземпляр класса AudioClipManager останавливает аудиоклип, на который ссьmается prevClip. Это гарантирует, что предыдущий затребованный аудиоклип останавливается перед началом

проигрывания следующего.

МОТИВЫ

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

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

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

©Один экземпляр класса должен быть доступен для всех клиентов этого

класса.

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

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

РЕШЕНИЕ

Шаблон Singleton достаточно прост, поскольку он содержит только один класс

(рис. 5. 17).

SingLeton -siпgLеtопIпstапсе

.. .

«constructol'» -SiпgLеtоп( ) «misc))

+getlnstance( )

.. .

Рис. 5.17. Класс-одиночка

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

Для доступа к одному экземпляру класса-одиночки класс предоставляет стати­ ческий метод, обычно с именем getInstance или getClassname, который

возвращает ссылку на единственный экземпляр класса.

РЕАЛИЗАЦИЯ

Хотя шаблон Singleton предполагает относительное простое решение, его реа­

лизация включает очень большое количество тонких моментов.

З а к р ыт ы й ко н стру кт о р

Чтобы подчеркнуть природу класса-одиночки, необходимо написать код для

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

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

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

бы один закрытый конструктор. Если класс вообще не объявляет никаких кон­

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

умолчанию.

«Л е н и в о е )) и н ста н ц и и ро в а н и е

Широко распространенный вариант шаблона S ingleton используется в ситуа­ циях, при которых экземпляр класса-одиночки может не понадобиться. Неиз",,:

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