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

GrandM-Patterns_in_Java

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

Adapter - 217

В нутр енние классы

Язык Java позволяет использовать в программах вложенные объявления классов, например,

public class FooХ ; private int

class Bar (

Хincrement ()

=х+l ;

}// increment ( )

// class bar

//class foovoid

Поскольку класс Bar задан внутри класса Foo, он считается частью клас­ са Foo и поэтому может ССЬVIаться на закрытые переменные экземпляры класса Foo. Когда экземпляр класса Foo создает экземпляр класса Bar, экземпляр класса Foo, который создал объект Bar, вызывается включаю­ щим его экземпляром класса Bar. Любые ССЬVIки в классе Bar на одну из переменных экземпляра класса Foo будут указывать на переменные включающего его экземпляра.

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

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

внутреннего класса.

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

кальным переменным содержащего его метода.

 

 

Внут

могут быть анонимными.

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

Примером такого класса может служить класс,

описанный

в разделе

« Реализацию>:

 

 

 

МenuItem exit = new MenuItem (caption) ;

 

 

 

exit. addActionListener (new ActionListener () public void actionPerformed(ActionEvent evt)

8Глава 7. Структурные шаблоны проектирования

close ( ) ;

} / / actionPerformed (ActionEvent )

) ;

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

слова «new», за которым следует имя расширяемого класса или реализуе­ мого интерфейса, аргументы конструктора и, наконец, тело класса в фи­

гурных скобках.

Представленный пример определяет анонимный внутренний класс, ко­ торый реализует интерфейс ActionLi stener. Он создает экземпляр

анонимного класса и передает его методу addActionLis tener объекта

Menul tem.

Более подробная информация о внутренних классах содержится в п. 8. 1 .2 « Спецификации языка Java» на сайте http://java.sun.com/docsjbooks/jls/ second_editionjhtmljjTOC.doc.html.

lЕДСТВИЯ

Классы Adaptee и Client остаются независимыми друг от друга.

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

ляющие отображать на экране и редактировать телефонные номера. этот

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

ляемые интерфейсом. Чтобы использовать интерфейс, необходимо опреде­

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

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

для считывания и хранения номеров пейлжера из экземпляров того ЧТОже

класса. Различие меЖдУ двумя классами адаптеров заключается в том,

они вызывают различные методы класса Adaptee. Рассмотрим на примере.

Предположим, что существует класс PhoneNumberEdi tor, который отве­

чает за то, чтобы разрешить пользователю изменять телефонный номер·

Объект передается конструктору класса PhoneNumberEdi tor, которы й

реализует этот интерфейс.

public interface PhoneNumberIF public Strinq qetPhoneNumber ( )

public void setPhoneNumber (Strinq newValue)

/ / inter face PhoneNumberIF

Adapter 8 219

Если нужно создать два объекта PhoneNumberEditor для изменения офис­ ных телефонных и факсовых номеров какого-то человека, то можно напи­ сать примерно такой код:

PhoneNumberEditor= voiceNumber;

voiceNumber new PhoneNumЬerEditor (new PhoneNumberIF () ( public Strinq qetPhoneNumЬer ( ) (

return person . qetOfficeNumЬer () ; ) // getPhoneNumber

public void setPhoneNumЬer (Strinq newValue) person . setOfficeNumЬer (newValue) ;

// setPhoneNumber ( String)

} )

PhoneNumberEditor faxNumЬer ;

faxNumber = new PhoneNumЬerEditor (new PhoneNumЬerIF () ( public Strinq qetPhoneNumber() (

return person . qetFAXNumЬ er () ;

}// getPhoneNumber

public void setPhoneNumber(Strinq newValue) ( person . setFAXN umЬer (newValue) ;

}// set PhoneNumber ( String)

}) ;

Каждый объект PhoneNumberEdi tor создается с новым адаптером. Каж­ дый адаптер вызывает разные методы одного и того же объекта.

€) Шаблон Adapter добавляет в проrpамму косвенность. Подобно любой другой косвенности, это усложняет понимание такой проrpаммы.

ПРИМЕНЕНИЕ В JAVA АР' И ПРИМЕР КОДА

Распространенный способ использования классов-адаптеров в Java API, пред­

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

Button ok = new Button ("OK") ;

ok . addActionListener (new ActionListener () public void actionPerformed(ActionEvent evt)

dolt () ;

}// actionPerformed (ActionEvent )

}) ;

add (ok) ;

220Глава 7. Структурныешаблоны проектирования

вэтом примере кода создается экземпляр анонимного класса, реализующий интерфейс ActionListener. При нажатии кнопки (объект Button) вызывает ся метод actionPerformedДЛЯэтого класса. Этот шаблон широко pacnpoCтpaHel{

вкодах, предназначенных обработки событий.

Java АРI не содержит каких либо oTKpbrrbIXj классов-адаптеров, готовых к ис пользованию. Он имеет классы, например ava . awt . event . WindowAdapter, предназначенные не для прямого использования, а для создания на их основе подклассов. Идея состоит в том, что некоторые интерфейсы приемника собы­ тий, например WindowListener, объявляют множество методов. Как правило, не все эти методы должны быть реализованы. Интерфейс WindowListener объявляет восемь методов, которые вызываются для оповещения о различных событиях, связанных с окнами. Часто только события одного или двух видов представляют интерес. Методы, соответствующие событиям, не представляю­ щим интерес, обычно делают пустыми. Класс WindowAdapter реализует ин­ терфейс WindowLi s tener и реализует все восемь его методов как бездействую­ щие. Класс адаптера, являющийся подклассом класса WindowAdapter, должен реализовывать только методы, соответствующие представляющим интерес со­ бытиям. Для всех остальных методов он наследует бездействующие варианты реализации. Например:

addWindowListener (new WindowAdapter () public void windowClosinq (WindowEvent е) (

Syster n . exit () ;

// windowClos ing (WindowEvent )

);

вданном примере кода анонимный класс-адаптер является подклассом класса WindowAdapter. Он реализует только метод windowClos ing и наследует от класса WindowAdapter бездействующие варианты реализации остальных семИ методов.

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

Facade. Класс Adapter предоставляет объект, действующий как промежуточ­ ное звено при обращениях к методам, осуществляемых между клиентскиМ"

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

тами и несколькими объектами, не известными клиентским объектам.

Iterator. Шаблон Iterator представляет собой специальную версию шаблоtJ8 Adapter, предназначенную для последовательного доступа к содержимому коЛ­ лекции объектов.

Adapter - 221

proxy. Шаблон Proxy, подобно шаблону Adapter, использует объект, который flвляется заменителем другого объекта. Однако объект Proxy имеет тот же ин­ терфейс, что и объект, заменителем которого он является.

Strategy. С точки зрения структуры шаблон Strategy аналогичен шаблону Лdарtег. Различие состоит в предназначении. Шаблон Adapter позволяет объекту Client выполнять при взаимодействии свою изначально предопределенную функцию, вызывая методы объектов, реализующих определенный интерфейс.

Шаблон Strategy предоставляет объекты, реализующие определенный интер­ фейс, с целью изменения или определения поведения объекта Client.

Anonymous Adapter. Шаблон Anonymous Adapter (описанный в книге [Grand99])

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

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

СИНОПСИС

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

КОНТЕКСТ

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

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

 

 

 

 

 

 

 

1

 

 

InventoryBrowser

 

1

 

 

Получает предметы инвентар" от него

 

 

 

L

 

 

 

 

 

 

1

 

 

 

 

 

 

 

 

 

 

 

 

отображает.....

 

«interface»

 

 

 

 

 

 

 

 

 

 

InventoryIteratorIF

 

 

 

 

 

 

 

*

 

 

 

 

 

 

hasNextInventoryItem() : booLean

 

 

 

 

I

InventOryIt

getNextInventoryItem() : InventoryItem

 

 

 

 

hasPrevInventoryItem() : bootean

 

 

 

 

 

 

*

 

 

 

 

 

 

getPrevInventoryItem() : InventoryItem

 

 

 

 

 

 

 

 

 

 

 

 

 

 

*-

 

 

Получает предметы инвентар" от Heгo

 

 

 

 

 

 

 

 

 

 

 

 

II

 

 

 

 

 

 

 

 

 

 

 

 

 

II

 

 

 

 

1 J

,

 

 

О 1

 

I

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

I

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

I

*

 

 

 

InventoryCollection

 

 

 

I

 

 

 

 

 

I

InventoryIteratorI

I

 

 

rIF

 

 

 

iterator( ) : InventoryIterato

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Рис. 7.4. Итератор инвентаря

Iteratar 223

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

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

InventoryCollection. Объект Inventory8rowser обращается к объекту

InventoryCollection не прямым образом. Вместо этого ему предоставляется объект, который реализует интерфейс Inventoryl teratorI F. Интерфейс

Inventoryl teratorIF определяет методы, позволяющие объекту последова­ тельно считывать содержимое коллекции объектов Inventoryltem.

МОТИВЫ

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

©Классу нужен универсальный способ доступа к содержимому множества

коллекций.

РЕШЕНИЕ

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

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

Collection. Класс в этой роли инкапсулирует коллекцию объектов или значений.

IteratorIF. Интерфейс в этой роли определяет методы для последовательного доступа к объектам, которые инкапсулированы в объекте Col lection.

Iterator. Класс в этой роли реализует интерфейс I teratorI F. Его экземпляры обеспечивают последовательный доступ к содержимому объекта Collection, связанного с объектом I terator.

CollectionIF. Обычно классы Col lection берут на себя ответственность за соз­ дание собственных объектов-итераторов. Очень удобно иметь универсальный

способ, позволяющий запрашивать объект Collection с целью создания объ­ ектаJlOCТ-b,итератора (I terator) для самого себя. Чтобы обеспечить эту универсаль­ все классы Col lection реализуютдля интерфейс CollectionI F, который

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

«interface» CoLLectionIF

iterator( ) : IteratorIF( )

' "

CoLLection

 

 

 

 

 

Создает

 

 

((interface»

 

 

IteratorIF

 

 

 

hasNextltemO : booLean

 

 

 

getNextltemO : Inventotyltem

Осуществляет выборку объектов'"

 

Iterator

 

 

 

Рис. 7.5. Шаблон Iterator

РЕАЛИЗАЦИЯ

До п ол н ител ь н ы е методы

Интерфейс объекта-итератора, представленный в разделе « Решение», содеРЖl1t минимальный набор методов. Интерфейсы объектов-итераторов, как правило, определяют дополнительные методы, если они нужны и если поддерживаются базовой коллекцией классов. Кроме методов, проверяющих наличие и считываю­

щих следующий элемент коллекции, часто используются следующие методы:

проверка наличия и считывание предыдущего элемента коллекции;

перемещение к первому или последнему элементу коллекции;

получение количества элементов обхода.

Внутре н н и й кл а сс

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

Пусто й ите р ато р

Пустой итератор - это итератор, который не возвращает никаких объектов. Его метод hasNext всегда возвращает false. Пустые итераторы обычно пред­ ставляют собой простой класс, который реализует соответствующий интерфейс итератора. Использование пустых итераторов может упростить реализаЦi1Ю классов коллекции и других классов итераторов, так как в этом случае нет неу­ обходимости в коде, предназначенном для обработки специального случая левого обхода.

Изме н е н и е б а з о в о й колл е к ц и и

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

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

сгенерировать исключение.

Iterator _ 225

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

памяти.

СЛЕДСТВИЯ

© Доступ к коллекции объектов возможен при отсугствии сведений об источ­ нике объектов.

© При использовании множества объектов-итераторов очень просто осушест­ влять и управлять несколькими обходами одновременно.

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

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

Классы коллекций в пакете j ava . util созданы в соответствии с шаблоном

Iterator. Интерфейс j ava . util . Collection играет роль CollectionIF. Пакет

содержит ряд классов, реализующих j ava . uti l . Collection.

Интерфейс j ava . uti l . Iterator выполняет роль I teratorIF. Классы этого

l(]JaCCbI,пакета реализующие j ava . util . Collection, определяют внутренние закрьrгые которые реализуют j ava . util . I terator и играют роль итератора.

ПРИМЕР КОДА

вlЦекачествей примера кода рассмотрим некоторый скелет программы, реализую­

проект, который описан в разделе «Контекст» . Листинг для интерфейса

InventoryI teratorIF:

public interface InventoryIteratorIF public boolean hasNextInventoryItem () public InventoryItem getNex InventoryItem ()

public boolean hasPrevInventoryItem() ;

//puыinterfacelcc Inv ntoryItemInventorylteratorgetPrevInventoryItem()

226

Глава 7. Структурные шаблоны проектирования

 

Приведем скелетный листинг для класса InventoryCollection. Листинг со.. держит метод iterator, используемый другими классами для получения объ. екта, необходимого для обхода содержимого объекта InventoryCol lection. Он включает в себя также закрытый класс, который инстанциируется методом iterator.

public class InventoryCollection {

public InventoryIteratorIF iterator () return new InventoryIterator () ;

} // i te rator ( )

private class InventoryIterator implements InventoryIteratorIF

public boolean hasNextInventoryItem ()

} // hasNext lnventoryl tem ( )

public InventoryItem getNextInventoryItem ()

} // getNextlnventoryl tem ( )

public boolean hasPrevInventoryItem()

} // has Prevl nventoryl tem ( )

public InventoryItem getPrevInventoryItem()

}// getPrevlnventoryl tem ( )

// class Inventoryl terator

//class InventoryCollection

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

С ШАБЛОНОМ ITERATOR

Adapter. Шаблон Iterator - специальная форма шаблона Adapter, предназна­

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

Factory Method.ДЛЯНекоторые классы коллекций могут использовать шабдоll

Factory Method принятия решения, итератор какого вида должен быть ИН"

станциирован.

Null Object. Пустые итераторы иногда используются для реализации шаблона

Null Object.

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