GrandM-Patterns_in_Java
.pdfЭтот шаблон впервые был описан в работе [RhieI2000).
СИНОПСИС
ДЛЯГарантирует согласованное поведение концептуально связанных классов, задавая них общий абстрактный суперкласс.
КОНТЕКСТ
Нужно написать классы Д Л Я предоставления последовательного доступа (толь ко для чтения) к некоторым структурам данных. Допустим, эти классы будут
реализовывать интерфейс j ava . util . Iterator.
Интерфейс Iterator содержит метод, который называется remove. В доку ментации сказане, что назначение метода remove - удалять объекты из источ ника после их извлечения. В описании метода remove сказано также, что этот метод необязательныЙ. Реализация данного метода может просто генерировать исключение UnsupportedOperationException.
Цель заключается в том, чтобы предоставить доступ только для чтения к струк турам данных, поэтому нужно, чтобы все классы генерировали исключение
UnsupportedOperationException. При вызове метода remove, чтобы быть уверенным, что все классы реализуют метод remove совершенно одинаково, не обходимо создать общий абстрактный классдля всех классов Iterator, от кото рого они будут унаследованы. Общий суперкласс реализует метод remove, ко
торый генерирует исключение UnsupportedOperationException при вызове
этого метода (рис. 4.9).
МОТИВЫ
©Нужно гарантировать, чтобы общая логика для связанных классов реализо
вывалась одинаково для каждого класса.
©Нужно избежать издержек, связанных со временем выполнения и поддерж
кой излишнего кода.
©
®
Нужно упростить написание связанныхво классов.
Нужно задать общее поведение, хотя многих ситуациях наследование не са мый подходящий способ его реализации (см. шаблон Delegation).
'6 8 Глава 4. Основные шаблоны проектирования
|
|
«interface» |
|
|
|
Iterator |
|
|
|
|
|
|
|
hasNextO : boolean |
|
|
|
next() : Object |
|
|
|
remove() : void |
|
|
|
|
|
|
|
*" |
|
|
|
II |
|
|
|
I |
|
|
|
I |
|
|
|
I |
|
|
|
I |
|
|
|
AbstractItera tor |
|
|
|
|
|
|
|
remove() |
|
|
|
|
|
|
|
1 |
|
ArrayIterator |
NullIterator |
|
|
|
|
|
|
Рис. 4.9. Итераторы и их абстрактный суперк.ласс
РЕШЕНИЕ
Реализуем общую логику связанных классов в суперклассе. Варианты поведения, зависящие от конкретного наследника, поместим в методы с одинаковой сиг натурой. Сделаем эти методы абстрактными в нашем суперклассе. На рис. 4. 1О представлена подобная структура.
Опишем роли, которые играют классы в рамках шаблона Abstract Superclass.
Abstract Superclass. Класс, выступающий в этой роли, представляет собой абст
рактный суперкласс, в котором инкапсулирована общая логика связанных клас
сов. Связанные классы расширяют этот класс. Таким образом, они могут на
следовать его методы. Методы с одинаковыми сигнатурами и общей логикой
для всех связанных классов помещаются в суперкласс, поэтому логика этих ме тодов может наследоваться всеми подклассами данного суперкласса. Методы
с зависящей от конкретного подкласса данного суперкласса логикой, но с оди
наковыми сигнатурами, объявляются в абстрактном классе как абстрактные
методы, тем самым гарантируя, что каждый конкретный подкласс будет иметь
методы с такими же сиmатурамии т.д..
ConcreteClassl, ConcreteClass2 Класс, выступающий в этой роли, представ ляет собой конкретный класс, чья логика и назначение связаны с другими
конкретными классами. Методы, общие Д Л Я этих связанных классов, помеща ются в абстрактный суперкласс.
Общая логика, которая не представлена в общих методах, помещается в общие
Abstract Superclass • 77
AbstroctSupercloss
соттопОрl соттопОр. 2
. .
voriontOpl vorionWpZ... ..
|
|
|
|
|
|
|
|
|
|
I |
|
|
|
|
I |
||
(oncreteCLassl |
|
|
(oncrete([ass2 |
|
|
.. |
||
|
|
|
|
|||||
|
|
|
|
|
||||
|
|
|
|
|
|
|
|
|
... |
|
|
... |
|
|
|
|
|
variantOpl |
|
|
variantOpl |
|
|
|
||
variantOp2 |
|
|
variantOp2 |
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
Рис. 4.10. |
Шаблон Abstract Superclass |
|
|
РЕАЛИЗАЦИЯ то
Если общие методы являются открытыми, можно поместить эти методы в ин терфейс, а абстрактный суперкласс будет имплементировать этот интерфейс.
СЛЕДСТВИЯ
© Тестирование будет занимать меньше времени, так как уменьшается коли чество кода.
® Использование шаблона Abstract Superclass приводит к появлению зависи мости между суперклассом и его подклассамидля . Изменение суперкласса мо жет иметь нежелательные последствия некоторых подклассов.
ПРИМЕНЕНИЕ В JAVA- API
Класс j ava . awt . AWTEvent это абстрактный класс Д Л Я классов, инкапсули
рующих события, связанные с GUI (Graphical User Iпtеrfасе, графический ин
терфейс пользователя). Он определяет несколько методов, которые являются общими ДЛЯ классов событий.
ПРИМЕР КОДА
в качестве примера рассмотрим реализацию классов из раздела Реализация этих классов взята из ПОorgфирмы. clickblocksClickBlocks,. которое вы можете найти на сайте этой книги в пакете
78 • Глава 4. Основные шаблоны проектирования
Листинг класса AbstractIterator: /**
*Этот абстрактный класс очень удобен ,
*поскольку позволяет определять итераторы
*путем реализации только одного метода getNextElement ( ) .
*/ abstract public class AbstractIterator implements Iterator (
private Object nextElementi
/ * * * Этот метод должен вызываться конструктором подкласса .
*/
protected void init () { nextElement = getNextElement () i
} // init ( )
/**
*Этот метод возвращает следующий элемент
*в просматриваемой структуре данных .
*Если следующего элемента нет , то возвращает сам себя .
*/
public abstract Object getNextElement () i
/** *
* /
Возвращает true , если в структуре данной итерации есть еще не пройденные элементы .
public boolean hasNext () { return nextElement!=this i
} // hasNext ( )
/**
* Возвращает следующий элемент в итерации .
* |
@except ion NoSuchElementException Итерация не имеет больше |
* |
элементов . |
*/
public Object next ( ) {
if" (nextElement=this)
Abstract Superclass - 79
throw new NoSuchElementException () i // i f =
Object previous= nextElementi nextElement getNextElement () i return previous i
// next ( )
/** * Удаляет из базовой коллекции последний элемент, возвращенный
*методом next .
*@except ion UnsupportedOpe rationException
*Если операция удаления не поддерживается этим
*итератором .
*/
public void remove () {
throw new UnsupportedOperationException () i
}// remove ( )
// class Abstractlterator
ШАБЛОНЫ ПРОЕКТИРОВАНИЯ, СВЯЗАННЫЕ
С ШАБЛОНОМ ABSTRACT SUPERCLASS
лInterfaceн and AЬstract Class. Шаблон Inteгface and Abstract Class использует шаб
о Abstract Superclass.
Template Method. Шаблон Template Method использует шаблон Abstract
Superclass.
:ИНОПСИС
lеобходимо сделать клиентские классы независимыми от тех классов, которые ,еализуют поведение, и обеспечить согласованность поведения между классами, ,еализующими это поведение. Не нужно делать выбор между использованием IНтерфейса и абстрактного класса. Можно иметь классы, реализующие некото ,ый интерфейс и наследующиеся от абстрактного класса.
I(OHTEKCT
ГIредположим, при проектировании приложения нужно сокрыть класс или <лассы, которые реализуют некоторое поведение, и поэтому их делают закры гыми и реализующими некий открытый интерфейс. Кроме того, чтобы такая Jеализация была логичной и удобной, классы должны наследоваться от общего lбстрактного класса. Но неизвестно, что сделать предком этих классов - ин герфейс или абстрактный класс.
МОТИВЫ
©При помощи шаблона Interface интерфейсы в языке Java могут использо ваться для сокрытия конкретного класса, реализующего поведение, от кли
ентов этого класса.
©Инкапсуляция общей логики с помощью шаблона Abstract Superclass в су
перклассе при написании классов помогает обеспечить связанность реали
зации. Можно также снизить объем работ по имплементации с точки зре ния повторного использования кода.
®Если специалисты имеют возможность использовать два разных способа
улучшения организации классов, то, как правило, тенденция такова, что
нужно выбирать либо тот, либо другой.
РЕШЕНИЕ
Шаблон Interface используют в том случае, если нужно сокрыть от клиентов класс некоторого объекта, предоставляющего определенный сервис. Определяют
косвенный доступ клиентских объектов к объекту, предоставляющему сервис, т.е. через интерфейс. Косвенность позволяет клиентам иметь доступ к объекту, предоставляющему сервис, ничего не зная о том, с объектами какого рода они имеют дело.
Interface and Abstract Class • 81
Если нужно проектировать набор связанных классов, обеспечивающих похо жую функциональность, то определяют общие части их реализации в абстракт ном суперклассе.
При возникновении двух этих требований в рамках одной задачи используют и интерфейс, и абстрактный класс (рис. 4. 1 1).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
I |
|
1 |
|
|
I |
|
|
|
|
|
|
|
|
|
|
|
ИСПОЛl.зует |
|
j |
|
|
|
|
|
|
|
|||
Client |
|
|
|
«interface» |
I |
|
||||||||
1 1 |
|
|
|
1 |
"\ |
+ServiceIF |
|
|||||||
|
|
|
|
|
|
|||||||||
|
|
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
I |
|
|
|
||
|
|
|
|
|
|
|
|
|
I |
|
|
|
||
|
|
|
|
|
|
|
|
|
I |
|
|
|
||
|
|
|
|
|
|
|
|
|
I |
|
|
|
||
|
|
|
|
|
|
|
|
|
I |
|
|
|
||
|
|
|
|
|
|
|
|
|
I |
|
|
|
||
|
|
|
|
|
|
|
|
|
I |
|
|
|
||
|
|
|
|
|
|
|
|
|
I |
|
|
|
||
|
|
|
|
|
|
|
|
|
I |
|
|
|
||
|
|
|
|
|
|
|
|
I |
, |
|
I |
|
||
|
|
|
|
|
|
|
|
|
r1 |
|
||||
|
|
|
|
|
|
|
|
|
Abstract5ervice |
|
|
|
||
|
|
|
|
ConcreteService1I |
|
|
|
|
|
. .I . |
||||
|
|
|
|
|
|
|
|
|
|
|||||
|
|
|
I |
|
I |
I |
|
|
J |
|||||
|
|
|
|
ConcreteService2 |
Рис. 4.11. Интерфейс и абстрактный класс
Если применяется подобная комбинация интерфейса и абстрактного класса, интерфейс должен быть открытым, а абстрактный класс, если существует такая
возможность, - закрытым.
СЛЕДСТВИЕ
©Использование шаблона Interface and Abstract Class позволяет получить все преимущества интерфейсов и абстрактных классов.
ПРИМЕНЕНИЕ В JAVA API
Пакет j avax . swing . table содержит интерфейсы и классы для работы с таб
лицами при проектировании интерфейса пользователя. Для каждой таблицы соответствующий объект модели данных содержит значения, отображаемые в таблице. Чтобы быть использованным в качестве модели данных для таблицы,
объект должен быть экземпляром некоторого класса, реализующего интерфейс
82 • Глава 4. Основные шаблоны проектирования
j avax . swing . table . TableModel. Кроме того, в этом пакете содержится класс AbstractTableModel. AbstractTableModel - это абстрактный класс,
который содержит некоторую логику, задаваемую по умолчанию и полезную при реализации методов, объявленных в интерфейсе TableModel. И наконец, существует конкретный класс DefaultTableModel, который инстанциируется по умолчанию при создании таблицы. Эти отношения показаны на рис. 4. 12.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
javax.swing.table |
I |
|
|
|
|
|
|
|
|
|
|
Использует |
|
|
|
|
«interface» |
|
|
I |
JТaЫe |
|
|
|
|
|
|
I |
|
||
|
|
I 1 |
|
1'1 |
|
1)I. |
|
||||
|
|
I |
|
|
|
J |
+TableModeL |
|
|
||
|
|
|
|
|
|
|
|
|
I |
|
|
|
|
|
|
|
|
|
|
|
I |
|
|
|
|
|
|
|
|
|
|
|
I |
|
|
|
|
|
|
|
|
|
|
|
I |
|
|
|
|
|
|
|
|
|
|
|
I |
|
|
|
|
|
|
|
|
|
|
|
I |
|
|
|
|
|
|
|
|
I |
|
|
I |
|
I |
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
+AbstroctTobleModel |
|
I +DefauLtТableModeL I
Рис. 4.12. Отношения внутри интерфейса javax.swing.tabIe
ПРИМЕР КОДА
Пример кода ДЛЯ шаблона Interface and Abstract Class состоит из интерфейса
и классов, предназначенных для управления структурой данных, которая на
зывается двусвязный список (doubIy linked list). Класс DoubleLinkedListMgr
выполняет различные операции вставки и удаления элементов двусвязного
списка. Класс DoubleLinkedListMgr не требует того, чтобы объекты из дву
связного списка были экземплярами какого-либо определенного класса. Он
только требует, чтобы все элементы реализовывали интерфейс DoubleLinkIF.
Существует абстрактный класс AbstractDoubleLink, который реализует ин
терфейс DoubleLinkIF. Расширение класса AbstractDoubleLink - это удоб
ный способ написания конкретных классов, которые могут обрабатываться
в двусвязном списке.
Эти классы и интерфейс входят в пакет org . clickblocks . dataStructure
ПО ClickBlocks, находящеroся на сайте этой книги.
Interface and Abstract Class _ 83
Двусвязиый список
Двусвязный список представляет собой структуру данных, представлен ную в виде некоторой последовательности, в которой каждый элемент со держит ссылку на последующий и предществующий элемент. На рис. 4.13 представлен пример такой структуры.
Преимущество двусвязного списка над массивом заключается в количе стве операций, которые нужны для вставки и удаления объектов. При вставке или удалении объектов массива нужно сдвигать все содержимое массива, начиная с места, куда вставлен или откуда удален элемент. Чем больше массив, тем больше в среднем времени потребуется на вставку и уда ление его элемента. Вставка и удаление объектов в двусвязном списке предполагает корректировку ссылок на предшествующий и последующий элемент.
Вставка или удаление элемента из двусвязного списка всегда требует од ного и того же количества времени независимо от того, сколько в нем элементов. Недостаток состоит в том, что при поиске n-го элемента в дву связном списке нужно просмотреть первые n элементов. Чем больше n, тем больше требуется времени. На поиск n-го элемента в массиве затра чивается всегда одно и то же количество времени, независимо от размера массива.
I |
|
first:Objeg |
|||||||
предшествующий элемент ..... |
|
|
|
|
|
|
|
||
I |
|
:Object |
|
|
|
||||
|
|
|
|
||||||
предшествующий элемент ..... |
|
|
|
|
|
|
|
|
|
I |
|
|
:Object , |
|
|
|
|||
|
|
|
|
||||||
предшествующий элемент ..... |
|
|
|
|
|
|
|
|
|
I |
|
|
LiI t;Obi !:t |
|
|
||||
|
|
|
|
I
следующий элемент .....
I
следующий элемент .....
I
следующий элемент .....
I
Рис. 4.13. Двусвяэный список
84 • Глава 4. Основные шаблоны проектирования
Ниже представлен листинг интерфейса DoubleLinkIF:
public/ * * interface DoubleLinkIF (
*
* Возвращает узел , следующий за данным узлом в связном */списке , или nul l , если данный узел - последний .
public DoubleLinkIF getNext () |
|
||
/** |
|
|
|
* |
|
Задает узел , KOTOPbrn должен |
следовать з а дaHHЬ узлом |
* |
|
в связном списке . |
|
* |
|
@param node |
|
* |
|
Узел, КОТОРЫЙ должен следовать з а данным узлом |
|
* |
/ |
в связном списке , или nul l , |
|
* |
если зтот узел - последний в списке . |
||
* |
|
||
public void setNext(DoubleLinkIF newValue) |
|||
/** |
|
|
|
* |
/ |
Возвращает узел, KOTOPbrn должен предществовать данному |
|
* |
в связном списке , или nul l , |
если это первый узел . |
|
* |
|
||
public DoubleLinkIF getPrev () |
|
||
/ * * |
|
|
|
* |
|
Задает узел , KOTOPbrn должен предществовать данному узлу |
|
* |
|
|
|
* в связном списке . |
|
||
* |
|
@param node |
|
* |
|
Узел , который должен предшествовать данному узлу |
|
* |
|
в связном списке , или nul l , |
|
* / |
если этот узел - первый в списке . |
||
* |
|
||
public void : setPrev (DoubleLinkIF newValue) |
|||
} // |
interface DoubleLinkIF |
|
А теперь - листинг абстрактного класса AbstractDoubleLink, который лизует интерфейс DoubleLinkIF.
public aЬstract class AbstractDoubleLink implements DoubleLinkIF (
private DoubleLinkIF previous ; private DoubleLinkIF next;