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

GrandM-Patterns_in_Java

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

Delegation - 65

2.Когда «(функциональный» класс создается как подкласс вспомогатель­ ного класса, то, как правило, необходимо использовать функциональ­ ность вспомогательного класса для реализации функциональности, присущей самой задаче. Делая это с помощью наследования, мы ослаб­ ляем инкапсуляцию «(функционального» класса.

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

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

®Делегирование может быть менее удобным, чем наследование, ввиду того

что при реализации придется написать больше кода.

®Делегирование делает класс менее структурированным, чем наследование.

Втех проектах, где структура классов важна, эта структурированность и от­

сугствие гибкости у наследования могут быть достоинством. Это часто спра­ ведливо для каркасов (frameworks) приложениЙ. Более подробно этот вопрос обсуждается при рассмотрении шаблона Template Method (см. главу 8).

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

©Для многократного использования класса или его расширения чаще ис­ пользуется делегирование.

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

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

РЕШЕНИЕ

Применяйте делегирование для многократного использования характеристик

поведения и расширения класса. Делегирование реализуется посредством на­

писания нового класса (делегата), который включает в себя функциональность

исходного класса (делегируемого), используя его экземпляр и вызывая его ме­ тоды (рис. 4.4).

 

 

 

 

l

И

 

Dеlеg

аt

I ____ ___

 

 

оr

__

пользователь

__

 

 

 

СПОЛЬЗ Т_________ l!I

 

------------.I

 

 

Delegatee

используемыи

 

 

 

 

Рис. 4.4. Делегирование

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

66

Глава

4.

OCHOBHble шаБЛОНbI проектирования

 

 

Данная диаграмма показывает, что класс, выступающий в роли Delegator, ис­ пользует класс, выступающий в роли Delegatee.

Делегирование решает более общие задачи, чем наследование. Любое расшире­

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

также быть выполнено при помощи делегирования.

РЕАЛИЗАЦИЯ

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

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

Interface.

СЛЕДСТВИЯ

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

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

widgetFactory.

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

Шаблон Law of Demeter (описанный в книге [Grand99]) угверждает, что

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

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

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

Delegation 67

Fabrication (также описанный в книге [Grand99]).

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

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

Заметим, что возможно и желательно использовать все эти стратегии одновре­ менно.

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

В Java API существует множество примеров делегирования. Это представляет собой основу модели делегирования собьrrий в языке Java. Согласно этой модел объекты источника событий отправляют собьrrия объектам приемника собы тий. Объекты источника событий обычно не принимают решения, что делап с собьrrием. Вместо этого они делегируют обязанность по обработке события

объектам приемника.

ПРИМЕР КОДА

В качестве примера делегирования рассмотрим систему, которая отвечает за от· слеживание проверенных частей багажа. Допустим, система включает классы представляющие сегмент рейсаl, багажное отделение и части багажа, как показа· но на рис. 4.5.

1: checkLuggage(}

:RightSegment

1.1: checkLuggage(}

:LuggageCompartment

Рис. 4.5. Проверка багажа

Класс FlightSegment содержит метод checkLuggage, который контролируе"

часть багажа, оформляемого на рейс (рис. 4.5). Класс рейса делегирует эту опе

рацию экземпляру класса LuggageCompartment.

Кроме того, делегирование широко применяется при реализации коллеКЦИI1

Рассмотрим диаграмму класса, представленную на рис. 4.6.

Класс LuggageCompartment поддерживает коллекцию других объектов. Клас

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

I

Сегмент рейса - это часть путешествия без пересадки.

68 Глава 4. Основные шаблоны проектирования

объектов, обычно делегируют эту ответственность другому объекту, например, экземпляру класса j ava . uti l . ArrayList. Поскольку реализация коллекции при помощи делегирования очень хорошо известна, отдельный класс коллек­ ции обычно не указывается на схемах проектирования.

LuggageCompartment

1

содержиТ.....

*

Luggage

Рис. 4.6. Багажное отделение

Приведем фрагменты кодов, которые реализуют проект, представленный на рис. 4.5. Сначала покажем класс FlightSegment, делегирующий операцию checkLuggage классу LuggageCompartment:

сlазз FlightSeqment {

LuggageCompartment luggage ;

void checkLuggage (Luggage piece) throws LuggageException { luggage . checkLuggage (piece) ;

// checkLuggage ( Luggage )

//class FlightSegment

Атеперь реализуем класс LuggageCompartment, делегирующий коллекцию

частей багажа классу ArrayList:

сlазз LuggageCompartment {

/ / Части багажа в классе

LuggageCompartment .

private ArrayList pieces

= new ArrayList () ;

void checkLuggage (Luggage piece) throws LuggageException {

pieces . add (piece) ;

} / / chec kLuggage ( Luggage) // class LuggageCompartment

Delegation - 69

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

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

и Proxy.

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

СИНОПСИС

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

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

предоставляющим данные и сервисы, но имеющим минимальное влияние на

клиентские классы. Это реализуется следующим образом: другие классы полу­ чают доступ к данным и сервисам через некоторый интерфейс.

КОНТЕКСТ

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

Необходимо, чтобы объекты класса AddressPanel могли получать и устанав­

ливать информацию об адресе бизнес-объекта в отдельном объекте. Очевидно,

что продавцы, транспортные компании и т.д. должны быть представлены раз­ ными классами. При этом возникает вопрос, как класс AddressPanel будет

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

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

информации.

Тогда для того, чтобы экземпляры класса AddressPanel могли взаимодейство­

вать с бизнес-объектами системы, необходимо, чтобы эти бизнес-объекты им­

плементировали наш интерфейс.

Используя предоставляемую интерфейсом косвенность, клиенты класса

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

 

 

 

 

 

 

 

 

 

« interface»

 

 

 

 

 

 

 

 

 

AddressIF

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

getAddressl

 

 

 

 

о

 

 

 

 

setAddressl

 

 

 

 

льэует

 

 

 

 

 

I

 

 

 

 

 

 

_сп

 

 

getAddress2

I --------

Il--_

_

-

1

setCity

АddressРапеL

 

 

И

 

 

 

-

getCity

 

 

 

 

 

 

 

setAddress2

 

 

 

 

 

 

 

 

 

getState

 

 

 

 

 

 

 

 

 

setState

 

 

 

 

 

 

 

 

 

getPostaLCode

 

 

 

 

 

 

 

 

 

ьI.

 

 

 

 

 

 

 

 

 

setPostaLCode

 

 

 

 

 

 

 

 

 

I

 

 

 

 

 

 

 

 

 

I

 

 

 

 

 

 

 

 

 

I

 

 

 

 

 

 

 

 

 

I

 

 

 

 

 

 

 

 

 

,

 

 

 

 

 

 

 

 

 

 

DataCLass

 

 

 

 

 

 

 

 

 

 

 

Рис. 4.7. Косвенность, осуществляемая через адресный интерфейс

МОТИВЫ

©Некоторый объект использует другой объект ДЛЯ получения от него данны или сервисов. Если наш объект ДЛЯ получения доступа к другому объект должен явно указать, к какому классу этот объект принадлежит, то возмож ность многократного использования нашего класса ухудшается из-за силь ной связанности.

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

®Конструкторы класса не могут быть доступны через интерфейс, потом

что интерфейсы в Java не имеют конструкторов.

РЕШЕНИЕ

Чтобы избежать зависимости классов (когда один использует другой), исполь

зуют интерфейсы, делая такую зависимость косвенной (рис. 4.8).

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

Client. Класс Client использует другие классы, которые реализуют интерфей

IndirectionIF.

IndirectionIF. Интерфейс IndirectionIF обеспечивает косвенность, что по

зволяет классу Client быть независимым от реализации класса, играющеГI

роль Service. Как правило, интерфейсы, выступающие в такой роли, являютс.

открытыми.

Service. Класс в этой роли обслуживает классы, выступающие в роли Client

'2 Глава 4. Основные шоблоны проектирования

1-

и

CLient

__

1

_____....1

с

п

ол

ь]еу

 

 

_

 

Т ___

 

 

+-

J

 

 

 

1

1

I

«interface» I +lndirectionlF

t:.

-Service

Рис. 4.8. Шаблон Interface

<лассы, играющие эту роль, в идеале являются закрытыми ДЛЯ классов вне сво­гo пакета. Если классы Service объявляются как закрытые, то ДЛЯ получения юстynа к ним классы, не относящиеся к данному пакету, вынуждены использо­ шть интерфейсы.

РЕАЛИЗАЦИЯ

Реализация шаблона Interface проста: определдля яем интерфейс для предоставле­ tf"ия сервиса, пишем клиентские классы доступа к сервису через интерфейс

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

у Jаvа-интерфейсов нет конструкторов, поэтому эти интерфейсы нельзя ис­ ГlОльзоватьдля сохранения ответственности класса за создание независимых от него объектов. В Java АР! есть класс j ava . lang . reflect . Constructor, кото­ рый можно использовать ДЛЯ создания объектов, не зная, экземплярами какого класса они будут.

СЛЕДСТВИЯ

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

кретной реализации другого класса.

®Подобно любой другой косвенности, шаблон Interface может усложнить

программу для понимания.

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

Java API определяет интерфейс j ava . io . FilenameFi lter. Этот интерфейс

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

файла. Этот метод возвращает trиe или false в зависимости от того, должен ли быть включен в список указанный файл. Java АР! определяет также класс

дляjava . awt . FileDialog, который может использовать объект FilenameFilter

фильтрации файлов для их последующего использования.

ПРИМЕР КОДА

Примером1 шаблона Interface является класс Addres s Panel . Интерфейс Addre ss F рассматривается в разделе «Контекст» . Приведем код для класса

AddressPanel :

class AddressPanel extends Panel (

private AddressIF data;

/ / Объект данных .

/ * *

 

* Зададим объект данных,

с которым будет работать эта панель .

* /

 

public void setData {AddressIF address) {

} / / setData (Addres s I F)

/* *

* Сохраним содержимое TextFields в объекте данных .

* /

public void !=аауе () { if (data null)

data . setAddressl (addresslField . getText (» ;

data . setPostalCode (postalCodeField . getText (» ;

/ / i f data

}/ / save ( )

// class AddressPanel

Заметьте, что при объявлении переменной data AddressPanel класса мы ука­ зали в качестве типа переменной не класс, а интерфейс.

Назначение шаблона Interface заключается в предоставлении интерфейса, обес­ печиваюшеm косвенность между клиентским и сервисным классами. Приведем

код для интерфейса AddressIF:

public interface AddressIF public String getAddressl () ;

public void setAddressl (String addressl) ;

public String getPostalCode () ;

public void setPostalCode (String PostalCode) ;

/ / interface Address I F

4Глава 4. OCHOBHble шаБЛОНbI nроектирования

!нтерфейс просто объявляет методы, которые требуются для получения необ­ )Димой информации.

lиже приведен код сервисного класса. Влияние шаблона Interface на класс огра­ ичивается тем, что класс должен реализовывать интерфейс AddressIF.

class ReceivingLocation extends Facility implements AddressIF{ private String addressl i

private String postalCode;

public String getAddressl () ( return addressl ; public void setAddressl= (String addressl)

this . addressl addressl ; } // setAddres s l ( String)

public String getPostalCode () ( return postalCode; public void setPostalCode= (String postalCode)

this .postalCode postalCode ; } // setPostalCode ( String)

// class ReceivingLocat ion

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

: ШАБЛОНОМ INTERFACE

)еlеgаtiоп. Шаблоны Delegation и Interface часто используются вместе.

dapter. Шаблон Adapter позволяет объектам, ожидающим отдругого объекта,

[то он реализует определенный интерфейс, работать с объектами, не реализую­ цими предполагаемый интерфейс.

;trategy. Шаблон Strategy использует шаблон Interface.

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

Iспользует шаблон Interface.

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