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

GrandM-Patterns_in_Java

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

Глава 3. Жизненный цикл программного обеспечения - 55

1.2.Объект TimekeepingController вызывает метод getEventType

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

1.3.Объект TimekeepingController передает тип события, полу­ ченный от пользовательского интерфейса, методу createEvent

класса TimekeepingEvent. Метод createEvent возвращает объ­ ект, в котором инкапсулировано событие.

1.4.Объект TimekeepingController передает объект события методу storeEvent объекта DаtаЬаsе для сохранения его в базе данных.

 

 

 

 

 

 

I start:StaгtShiftEvent I

id:EmDioyeeID

 

emp:Empioyee

:UserInterface

1.2:

1: doTransaction(id)

 

 

 

 

eventType,.

:8 getEvent

Тype( )

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

l

11.з:

 

 

 

 

 

 

:

1.4:

 

 

 

 

г

----

г---storeEvent(start).L---..,

 

 

1:

етр : iookuPEmPioyee(id)

 

start - createEvent(eventТype)

 

 

 

Database

 

 

 

 

 

 

:TimekeepingEvent

 

Рис. 3.7. Диаграмма взаимоцейcrвия для случая «начало смены))

Рассмотрим некоторые особенности структуры диаграммы взаимодействия,

представленной на рис. 3.7.

Операция lookupEmployee класса Database создает объект для инкапсу­ ляции найденной информации о служащем. Шаблон Creator, описанный

в книге [Grand99], угверждает, что если объект содержит, агрегирует, запи­

сывает экземпляры класса или осуществляет инициализацию данных для экземIU1ЯРОВ класса, то такой объект является хорощим кандидатом на роль

создателя экземпляров этого класса. Поэтому объекты, которые создает операция lookupEmployee класса Database, будуг экземплярами класса

Employee.

Событие «начало смены,) является разновидностью события системы учета

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

также и другие подклассы класса TimekeepingEvent, представляющие

другие виды событий системы учета рабочего времени. Не нужно, чтобы

56

Глава 3, Жизненный цикл программного обеспечения

класса TimekeepingEvent, так как задача - сделать зависимости между пользовательским интерфейсом и внутренней логикой минимальными. Для решения этой задачи используется шаблон Factory Method.

Шаблон Factory Method делает один класс отвечающим за создание экземпляров других классов, имеющих общий суперкласс или реализующих общий интер­ фейс. В соответствии с этим шаблоном делегируем классу TimekeepingEvent обязанность по созданию экземпляров его подклассов.

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

UserInterface

Использует символические имена, задаваемые им

gеtЕvепtТуре( ):int

 

 

 

 

 

I

1

 

 

 

 

 

 

 

 

 

 

Использует

 

 

 

 

 

 

 

 

 

1

 

 

 

 

 

 

 

 

 

ТimеkеерiпgСопtгоllег

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

doТгапsасtiоп(еmрlоуееID:Striпg)

 

 

 

 

 

 

 

1

 

1

 

 

 

 

 

 

 

 

инициатор запроса

 

 

 

ию

О

 

 

 

 

 

 

 

 

 

Запрашивает собы

о созда

н

 

 

 

 

 

тие п

 

 

 

1

 

 

объекта

 

 

 

 

 

 

......аписЗ ывает и считывает ДЛЯ него данные

 

 

 

 

 

 

 

 

 

 

создатель

О ,Ir

ТimekeepingEventО

srART_SHIFТ:int2 - {frоzеп} srART_BREAK:int3 - 1 {frozen} END_SHIFТ - {frozen} END_BREAK - {frozen}

getEmployee( ) : Employee getтime( ):Date

!;reate v!:nt(eventT l1e :int):Тimek!:el1i ngEvent

Database

Employee

 

lookupEmployee(employeeID:String):Employee

 

 

stогеЕvепt(еvепt:ТimеkеерiпgЕvепt)

getEmployeeID:String

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

исполнитель

1

1

исполнитель

 

 

 

 

 

 

 

 

 

 

Играет роль

.....

 

 

 

PayPeriod

 

 

 

 

 

 

 

РаугоlLSystem

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

getStart( ):Date

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

getEnd( ):Date

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

TimekeepiпgRероrt

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Тimekeepinglog

 

Break

 

Shift

 

 

 

Глава 3. Жизненный цикл программного обеспечения 8 57

ИЗ проекта был удален класс EmployeeBadge, поскольку механизм получения идентификационного номера сотрудника - это часть пользовательского ин­ терфейса, а не внутренней логики.

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

венность была возложена на класс Database. А это значит, что в нашем проек­ те класс TimekeepingLog не нужен, поэтому удаляем его.

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

отформатированные отчеты поступают в систему платежных ведомостей.

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

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

При расчете зарплаты служащего время смены делится на три категории:

1. Обычное время, оплачиваемое по стандартной почасовой ставке работника.

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

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

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

И других вычетов.

Правила классификации при определении, ЯRЛяется ли время обычным, сверх­

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

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

лате сверхнормативного времени. В настоящее время (,Продовольственная сеть Генри» функционирует в пределах только одного штата США. Но разрабатыва­

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

В других штатах. Поэтому проект должен учитывать различные правила, приме­

Няемые при классификации рабочего времени и вычислении оплаты труда слу­

жащего.

Чтобы иметь возможность выбора различных наборов правил для выполнения

Вычислений по учету рабочего времени, необходимо определить способ орга­

низации набора правил как набора объектов. Набор правил можно представить

в виде конечных автоматов, на входе которых - события системы учета рабочего

58 Глава 3. Жизненный цикл программного обеспечения

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

ни ДЛЯ штата Джорджия.

о

reguLarHours=O overtimeHours-о

uпраidНоuв-О reguLarPay-О;оvеrtimеРау-о

 

 

 

Начало оплачиваемого

 

 

 

 

 

 

периода

 

Начало смены

 

 

 

Начало перерыва

Начало перерыва

 

Начало смены

 

Enter / breakTime - event.getТime( )

 

 

 

Enter / iпсоmрLеtеShift-faLsе

конецL

 

 

 

 

 

 

startTi me-еvепt.gеtТimе( )

 

Начало перерыва

 

 

 

 

 

 

 

 

 

 

 

 

 

перерыва

Конец перерыва

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

I

 

 

 

Конец смены

 

Начало смены

Enter/ unpaidHours +.. event.getТime( )-ЬrеаkТimе

 

 

 

 

 

 

 

reguLarHours--еvепtТimе( )-ЬrеаkТimе

 

 

 

 

 

 

 

 

 

 

r

 

Конец

 

......

 

 

 

 

 

 

 

 

 

 

смены

 

 

 

 

Enter / reguLarHours +-еvепt.gеtТimе( )-startТimе

 

 

 

Конец смены

if (reguLarHourS>40) {

 

 

 

 

 

 

 

 

 

overtimeHours += reguLarHours - 40;

 

 

 

 

 

 

 

 

 

 

 

 

reguLarHours - 40

 

 

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 

 

 

reguLarPay .. reguLarHours * payRate;

 

 

 

"-

 

overtimePay - overtimeHours * payRate*1.5

 

 

 

 

createShift( )

./

 

 

 

 

 

 

 

 

 

 

 

Рис. 3.9. Пример вычислений в системе учета рабочего времени,

выполняемых на основе состояний

Для реализации конечного автомата (рис. 3.9) можно воспользоваться шабло­

ном State, который реализует состояния класса состояний в виде классов, реа­

лизующих общий интерфейс. Диаграмма классов, изображенная на рис. 3. 1 О,

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

Глава 3. Жизненный цикл программноro обеспечения - 59

«interface» ТimekeepingStateIF

+getReguLarHours( ) : int +getOvertimeHours( ) : int +getUnpaidHours( ) : int +getReguLarPay( ) : int +getOvertimePay( ) : int +getShifts( ) : Enumeration +start( ) : 1imekeepingStateIF

+processEvent(:1imekeepingEvent) : ТimekeepingStateIF

GAТiтekeepingState

+getReguLarНours( ) : int +getOvertimeHours( ) : int +getUnpaidHours( ) : int +getReguLarPay( ) : int +getOvertimePay( ) : int I/enter( )

I/createShift( ) +getShifts( ) : Enumeration

+start( ) : TimekeepingStateIF

 

 

 

 

 

I

 

 

I

 

 

GдStartBreak

 

 

GAEndShift

 

 

 

 

I/enter( )

 

I/enter( )

+processEvent(:тimekeepingEvent)

 

+processEvent(:TimekeepingEvent)

 

: GATimekeepingState

 

: GAТimekeepingState

 

 

 

 

 

I

 

 

r

 

 

 

GдStartShift

 

 

GAEndBreak

 

 

0 0 0

 

I/enter( )

I/enter( )

+processEvent(:1imekeepingEvent)

+processEvent(:ТimekeepingEvent)

 

: GA1imekeepingState

: GAТimekeepingState

Рис. 3.10. Классы состояний системы учета рабочего времени

Покажем, как используются классы, изображенные на рис. 3. 1 0. Программа,

Производяшая выборку событий системы учета рабочего времени служаших из

базы данных, создает объект GATimekeepingState. Для представления каждо­

го состояния конечного автомата этот объектGATimekeepingStateиспользует экземпляр каждого своего подкласса. Метод start объекта возвращает на­

чальное состояние конечного автомата и делает это состояние текущим.

60 •

ГПО80.1 Жизненнымцикп npoгpaммнoro06ecnеченИR

 

 

 

она

 

ТOI'O

как программз СОэдaJlа эхзсмnляр класса GАТimеkеерiлq5tr.:.е,

После

 

 

 

 

вызывает метод 9tart

 

объекта

 

считывания Н8Чanьноl'O состоя­

ния. Затем npoJ1)aIotM8

начинает считывать события системы учета рабочего

 

 

ЭТОI'O

 

ДIlЯ

 

 

 

 

 

времени

из

базы данных.

Каждый обнаруженный объект событиисистемы учета

состояния. Эroт метод зacra&IIяет конечный автомат выполнить

 

текушего

рабочего времени npoJ1)aMMa передает методу processEvent объекта

 

 

гое

 

 

 

 

 

 

 

 

 

 

 

 

перехОд в дру­

состояние, которое соответствует переданному ему типу

соБЫ'l1Ul

системы

 

 

 

 

учета рабочего времени. Метод processEvent воовpauuunновое текушее со­ стояние. Когда этот метод хочет зш:тавить конечный автомат перейти в некото­ рое сост ояние, он вызывает метод enter объсJCТа состояний.

По мере перехода конечного автомата ИЗ

 

состояния

 

он подсчи­

 

зарплаты. Кроме того, он объединиет

тывает рабочее время служаших и размерОДНОI'O

 

 

 

Вдpyroe

 

 

события СИстемы учета времени, образуя смены. Когда ПРОJ1)аммз заханчивает

передачу соБЫТИЙ конечному автомату,

она может вызвать соответствуюшие

JCТа GATimekeeplnqState

получения сведений о рабочих часах

Meтoдbl объеразмере оплаты или перечнеДIlЯрабочих смен

(

Епumеrаtiол

.

сл)'жашсго,

 

 

 

 

)

 

На этом закончим ИСtлсдование данной пpoбnемы. Теперь вы дOJlЖИ..Ыиметь предcтa вnение об использоваНИИ шаблонов проектироваН ИII.

-,

 

(: ,?

ГЛАВА

:;;

1,

 

 

IJ

 

Основные

wа6nоныпроектирования

 

 

 

D<lepdoD (д....."ро......) (62)

 

 

ID!ertiJce (Ивтерфе8с) (70)

 

 

АЬsuш Saperdoss\ cyпepкJIIIсс) (75)

1nta1ace .od AbstnIс! CIaSS(Ивтерфе8с aбnpamo.oIowcc)(80)

IlIIIIIU!abIe(Неюменвыll)(86)

МШer IDterface (М_витерфеАс) (91)

ProIQ' (3aмecnnu(96)ь

Шаблоны, преДСТ8ВЛСННЫС В данной главе, самые фуидамеН'I"dJ]ьные Н важные. Они активно используются другими шаблонами.

Шаблоны I:klegatlon, Interface. Abstract Superclass и lnterfacc ancl Abstracl Class

показы8З.ЮТ, как организовать отношения между клас;с8ми. Большинство шаб­ ЛОНОВ используют ХОТЯ бы один ю этих шаблонов. Они H8CТOnЬKO популярны, ЧТО чacro даЖе не упоминаются в разделе .Шаблоны, СВJlззнные с данным шаб­ ЛОНОМ проектировання» при описании большинства шаблонов.

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

тупа мноroчнсленных объеКТОВ к одному н тому же 06ъскту. Хотя шаблон

IrnmutabIc ЯВНО не является чаcrью ПО.!1aВ1Ulюшеro большинства дрyrиx шабло­

НОВ, он испольэустся пренмушественно с другими шаблонами.

Шаблон Markcr InLerface рассматривает способ упрошения проектнровання к.. aCCOB.которые имеют постоянный атрибyr типа boolean.

Шаблон Proxy является основой ШlМ многих шаблонов, рсализуюших лопtку управления ДОСТУПОМ одноro объекта к дрyroМУ достаточно .прозрачНl..МoI" спо­

uбoМ.

СИНОПСИС

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

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

универсальный способ расширения классов. Делегирование применимо во

многих ситуациях, где наследование терпит фиаско.

КОНТЕКСТ

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

Наследование, например, подходит для описания отношений «is-a-kind-of» (<<Это разновидность.. .»), так как они очень статичны по своей природе. Одна­ ко отношения «is-a-role-played-by» (<<Это роль, которую играет.. . ») неудобно моделировать при помощи наследования. Экземпляры класса могут играть сразу несколько ролей. Рассмотрим в качестве примера систему заказа авиабилетов.

Для нее характерны такие роли: пассажир, агент по продаже билетов и персо­

нал, обслуживающий рейс. Эти роли можно представить следующим образом:

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

(рис. 4. 1).

Person

TicketAgent

Рис. 4.1. Моделирование ролей при помощи наследования

ЧТОПроблема, связанная с изображенной на рис. 4. 1 диаграммой, состоит в том,

один и тот же субъект может играть несколько ролей. Субъект, который

Delegation 63

ВХОДИТ В обслуживающий персонал, может быть также и пассажиром. Некоторые авиалинии иногда направляют персонал компании на работу по учету билетов. Это значит, что один и тот же субъект может играть любую из указанных ролей. При моделировании этой ситуации ДЛЯ класса Person необходимо создать семь подклассов (рис. 4.2). Количество нужных подклассов экспоненциально воз­ растает при увеличении количества ролей. Например, чтобы смоделировать все возможные комбинации для шести ролей, необходимо создать 63 подкласса.

PersonJ

 

 

I

CrewMemberI

 

I

 

 

 

 

 

 

 

 

 

 

I

I

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

I

 

Passenger

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

I CrewMemberAndTicketAgent

I

 

 

 

 

 

 

 

 

 

 

I

 

TicketAgent

I

 

I

I

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1icketAgentAndPassenger I

 

 

 

I CrewMemberAndPassenger

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

I

CrewMemberAndTicketAgentAndPassenger

I

 

 

 

 

 

 

Рис. 4.2. Моделирование множеcrва ролей при помощи наследования

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

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

Согласно данной структуре, объект Person делегирует ответственность по вы­

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

назначен ДЛЯ выполнения этой роли. Таким образом, в системе вам потребуется определить столько объектов, сколько существует ролей. Различные их комби­

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

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

I

CrewMember

I

 

I

TicketAgent

I

 

I

Passenger

I

Испольэует

...

 

0..1

 

Использует'"

 

0. .1

 

 

 

 

 

 

0• . 1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1

J

 

,

 

1

 

1.

1

 

 

 

 

 

 

 

 

 

 

 

Person

 

 

 

 

 

 

 

 

 

 

 

 

"1

1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Рис. 4.3. Моделирование ролей при помощи делегирования

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

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

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

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

моти в ы

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

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

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

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

тодов и переменных, унаследованных от суперкласса. С другой стороны,

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

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

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

со временем.

© «(Функциональный» класс (класс, имеющий отношение к функциональности программы) не должен быть подклассом вспомогательного класса. Сущест­ вуют по крайней мере две причины не делать этого:

1. При объявлении класса подклассом таких классов, как ArrayList или HashMap, есть риск того, что эти классы могут измениться впоследствии и стать несовместимыми с ужпредыдущими версиями. Хотя риск этого невелик, обычно нет таких видимых причин идти на этот риск.

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