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

GrandM-Patterns_in_Java

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

Coche Monogement - 297

раскрываются при описании некоторых шаблонов в книге [Grand2001). Они

упоминаются в разделе «Шаблоны проектирования, связанные с шаблоном

Cache Management.) в конце описания данного шаблона.

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

менения соответствующим образом будyr отражены в течение определенного промежутка времени. Более подробно этот подход рассматривается при описа­ нии шаблона Ephemeral Cache Item, которое можно найти в книге [Grand2001).

ПРИМЕР КОДА

Предположим, необходимо написать ПО ДЛЯ системы учета рабочего времени служащих. Система состоит из терминалов и сервера учета рабочего времени. Терминалы представляют собой небольшие устройства, вмонтированные в стену обфиса. Когда служащий приходит на работу или уходит с работы, он оповещает этом систему учета рабочего времени, проводя идентификационной карточ­ кой через терминал учета времени. Терминал считывает идентификационный номер служащего и подтверждает правильность карточки, показывая на экране имя сотрудника и варианты выбора. Затем служащий нажимает кнопку, чтобы

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

платежных ведомостей фирмы получает от системы учета рабочего времени ко­ личество часов работы каждого служащего и подготавливает чеки на оплату.

Все детали информации, которую работник видит на экране, зависят от профиля

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

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

Большинство фирм закрепляют за своими служащими определенное рабочее

место. Такие служащие обычно используют ближайший к своему месту работы

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

ПОльзовался менее чем 70 служащими, имеющими определенное рабочее место.

З

 

 

 

 

 

 

 

 

 

начит

 

 

 

 

 

 

 

 

 

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

СТ

 

имость

терминалов. Чтобы быть дешевыми, терминалы должны иметь па­

 

О

 

 

 

 

М

 

 

ь

минимальных размеров. Это означает, что придется задать довольно уме­

р

ят

 

 

е

н

ный макс;;rмальный размер кэша

.

С другой стороны, для поддержки высо­

Кой

 

скорости реагирования желательно, чтобы терминалы сохраняли в кэше

параметры служащих и почти всегда немедленно реагировали на предоставлен­

ную

идентификационную карту. При у

 

 

 

 

становлении первоначального максималь­

НОГО размера кэша за основу принимается рекомендация, согласно которой

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

расположение терминалов должно быть таким, чтобы одним и тем же теРМинаНа­

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

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

Выбор значения, превышающего 70, объясняется тем, что в некоторых случаях

одним и тем же терминалом могут воспользоваться более 70 служащих. Иногда,

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

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

например, обслуживающий персонал, перемещаются С одного рабочего места на другое.

На рис. 7.34 представлена диаграмма классов, которая демонстрирует решение

этой проблемы при помощи шаблона Cache Management.

EmpLoyeeProfiLeManager

 

Выбирает параметры

 

1

служащих ДЛ" него

1

fetcf1EmpLoyee(EmpLoyeeID)

 

 

 

1

помещающий в кэш

 

 

 

 

 

Выборка удаленных параметров'.....

 

1

служащих ДЛ" кэширования

I

EmpLoyeeID

проиэводящий выборку

 

 

 

EmpLoyeeProfiLeFetcf1er

 

 

 

 

 

fetcf1EmpLoyee(EmpLoyeeID}

EmpLoyeeProfiLeCacf1e addEmpLoyee(EmpLoyeeProfiLe ) fetcf1EmpLoyee (EmpLoyeeID )

1)

1

 

----------

Помещает.....

в кэш

 

 

0..*

I

I EmpLoyeeProftLe

Рис. 7.34. Управление кэшем дnя системы учета рабочего времени

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

времени. Сначала приведем код для класса Emp l oyeePro f i l eManager:

clas8 EmployeeProfileНanager (

private EmployeeCache сасЬе = new EmployeeCache () ;

private EmployeeProfileFetcher server

/ * *= new EmployeeProfileFetcher () ;

*

Из внутреннего кэша или сервера учета рабочего времени

( если нет во внутреннем кэше ) извлекаем

*

профиль служащего , соответствующий данному

 

идентификационному номеру .

 

 

 

 

 

 

 

 

 

Coche Monogement 299

*

 

@ return

Возвратить профиль

служащего или nul l , если

 

параметры не найдены .

 

 

 

 

*/

 

 

 

 

 

 

 

 

EmployeeProf11e fetchEmployee (EmployeeID id) (

 

EmployeeProfile profile = cache . fetchEmployee (id) ;

 

if (profile == null)

(

//

Если нет в кэше , пытаемся

 

 

 

 

 

 

 

//

выполнить выборку из сервера .

 

 

profile = server . fetchEmployee (id) ;

 

 

if

(profile ! = null)

(

//

Получаем профиль из сервера

 

 

 

 

 

 

 

 

//

и помещаем профиль в кэш .

 

 

 

cache . addEmployee (profile) ;

 

 

}

//

i f

! = null

 

 

 

 

 

 

//

i f

==

null

 

 

 

 

//fetchEmployee (EmployeeID)

//class EmployeeProfileManagerreturn profile;

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

кэш. Она использует также классы SoftReference, которые рассматрива­

лись в разделе «Реализация».

class/** EmployeeCache

*Для определения наименее использовавшегося

*за последнее время профиля служащего применяется связный

*список . Сам по себе кэш реализуется

при помощи объекта Hashtable . Значения Hashtable

*представляют собой объекты связного

*списка , которые ссылаются на реальный объект EmployeeProfile .

*/ = new Hashtable () ;private Hashtable cache

/**

*

Это головной узел связного списка, который ссылается

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

*

EmployeePro file .

*/

 

LinkedList mru = null ;

 

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

 

300

 

 

 

 

е

 

 

 

/**

 

 

 

 

 

 

*

 

Это последний узел связного списка,

KOTopbrn ссылается

 

 

*

 

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

 

 

*

 

EmployeePro f i le .

 

 

 

 

*/

 

 

 

 

 

LinkedList lru = null;

 

 

 

/**

 

 

 

 

 

 

*

 

Максимальное количество объектов EmployeeProfile,

 

 

 

*

 

которое может находиться в кэше .

 

 

 

 

*/

 

 

 

 

 

private final int МAX_CACНE_SIZE = 80 ;

 

 

 

/**

 

 

 

 

 

 

*

 

Количество объектов EmployeeProfile,

находящихся в

данный

 

 

*/

момент в кэше .

 

 

 

 

 

 

 

 

 

private int currentCacheSize = О ;

 

 

 

/**

 

 

 

 

 

 

*

 

Этот объект управляет очисткой после

того , как программа

 

 

*

 

сборки мусора решает, что пришло время освободить память ,

 

 

*

 

занимаемую объектом EmployeeProfile .

 

 

 

private CleanupQueue myCleanup = new CleanupQueue () ;

 

 

/

*

*

Этому методу передаются объекты, предназначенные

 

 

 

 

 

 

 

*

 

для добавления в кэш . Однако этот метод на самом деле может

 

 

*

 

не

добавить объект в кзш, если это противоречит его

политике

 

 

 

добавления объектов . Этот метод может также удалять

 

 

 

*

 

находящиеся в кэше объекты, чтобы освободить место

 

 

 

 

для новых .

 

 

 

 

*/

 

 

 

 

 

public void addEmployee (EmployeeProfile етр ) {

 

 

 

 

EmployeeID id = e m p . getID ( ) ;

 

 

 

 

 

if

(сасЬе . get (id) == null) { // Если

нет в кэше .

 

 

 

 

//

Добавляем профиль в кэш,

 

 

// делая его наиболее используемым за последнее время . if (currentCacheSize == О)

// Рассматриваем пустой кэш как особьrn случай .

 

 

 

Cache Management - 301

lru = mru = new LinkedList (emp) ;

else

{

/ /

currentCacheSize > О

LinkedList newLink;

>= МAX_CACНE_SIZE)

if

(currentCacheSize

 

// Удаляем наименее использовавшийся за последнее

 

/ /

время объект EmployeeProfi le из кэша .

 

 

newLink =

lru;

 

 

 

 

lru = newLink .previous ;

 

 

cache . remove (id) ;

 

 

 

сurrепtСасhеSizе- ;

 

 

 

lru . next =

null;

 

 

 

newLink . setProfile (emp) ;

 

 

else (

= new LinkedList (emp) ;

 

 

 

newLink

 

 

) / /

i f >= МАХ

САСНЕ

S I Z E

 

 

newLink . next

 

m ж u;

 

 

 

mru .previous =

 

 

 

newLink ;

 

 

newLink . previous

= null ;

 

 

mru =

newLink;

 

 

 

 

 

/ / i f

О

 

 

 

 

 

/ /

Помещаем профиль

служащего , наиболее

 

/ /

часто использовавшийся в последнее время , в

кэш .

cache . put (id, mru) ;

 

 

 

currentCacheSize++;

 

 

 

else (

 

 

 

/ /

Профиль уже в кэще .

 

/ /

Метод addEmployee не

должен вызываться , если

объект

// уже находится в кэше . Если это случится ,

// то производится выборка ,

// чтобы объект стал наиболее используемым

// за последнее время .

 

fetchEmployee (id) ;

 

/ / i f cache . get ( id)

}

/ / addEmployee (EmployeeProf i le )

/* *

*

Возвращает объект EmployeeProf i l e, связанньrn с данным

*

EmployeeID, или nul l ,

*

если н е найден такой EmployeePro f i le .

* /

EmployeeProfile fetchEmployee (EmployeeID id) {

)2 Глава 7.

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

 

//

Удаляем из Rэша люБОЙ Employee ID, соответствующий

 

//

объеRТ EmployeeProfi le

RОТОРОГО был удален при сБОРRе

 

//

мусора .

 

 

 

 

 

 

 

myCleanup . cleanup () ;

 

 

 

 

 

LinkedList foundLink =

(LinkedList) cache . get (id) ;

 

if

(foundLink == null)

 

 

// Не

в Rэше .

 

 

 

return null ;

 

 

 

if

(mru

! =

foundLink)

{

 

 

 

 

 

 

if

(

foundLink == lru

)

{

 

 

 

 

 

lru =

foundLink . previous ;

 

 

 

 

 

lru . next

= null;

 

 

 

 

 

 

 

}

//

i f

l ru

 

 

 

 

 

 

 

 

if (foundLink .previous ! = null)

 

 

 

 

foundLink . previous . next = foundLink . next;

 

 

 

if (foundLink . next !=

null)

 

 

 

 

 

foundLink . next .previou8 = foundLink .previous ;

 

 

 

mru .previous = foundLink;

 

 

 

 

foundLink . previou8 = null;

 

 

 

 

foundLink . next = ш ru;

 

 

 

 

 

 

mru =

foundLink;

 

>

1

 

 

 

 

//

i f

currentCacheSize

 

 

return foundLink . getProfile () ;

 

/*

//

fetchEmployee ( EmployeeID)

 

*

 

 

 

 

 

 

 

 

 

 

*

 

Удаляет

объеRТ EmployeeProfile,

связанньrn с данным

*

 

EmployeeI D, находящимся в

Rэше .

 

*/

 

 

 

 

 

 

 

 

 

 

void removeEmployee (EmployeeID id)

{

 

LinkedList foundLink =

(LinkedList) cache . get (id) ;

 

if

(foundLink

!= null)

{

 

 

 

 

 

 

if

(mru ==

foundLink)

{

 

 

 

 

 

 

mru = foundLink . next;

 

 

 

 

}

//

i f

mru

 

 

 

 

 

 

 

 

if

(

foundLink == lru

)

{

 

 

 

 

 

lru =

foundLink .previou8 ;

 

 

 

 

}

//

i f

lru

 

 

 

 

 

 

 

 

if (foundLink .previous != null)

 

 

 

 

foundLink . previous . next =

foundLink . next;

Cache Мапаgеmепt 303

 

 

} //

i f foundLink . previous

 

 

if (foundLink . next != null)

 

 

foundLink . next . previous = foundLink . previous ;

 

 

// if

! nu l l

 

 

//

i f foundLink . next

 

// removeEmployee (EmployeeID)

/**

 

 

 

*

 

Закрытый класс двусвязного списка для управления

*

 

списком наиболее использовавшихея за последнее время

*

 

профилей служащих . Этот класс реализует интерфейс

 

CleanupIF,

поэтому его экземпляры могут быть извешены

*

 

после того ,

как программа сборки мусора

*/

решит удалить объект EmployeeProfile .

 

 

private class LinkedList lements CleanupIF { SoftReference profileReference;

LinkedList previous ; LinkedList next;

LinkedList (EmployeeProfile profile) {

//setProfile (profile) ; constructor (EmployeeProfile)

void setProfile (EmployeeProfile profile) { profileReference =

//myCleanup . createSoftReference (profile , this) ;

}setProf ile ( EmployeePro file, Employee ID)

EmployeeProfile getProfile () {

}

// getProfile ( )

 

return (EmployeeProfile) profileReference . get ( ) ;

/ * *

*

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

*

которьм его реализует , удаляет сам себя из любой

*

структуры данных , частью которой он является .

,

public void extricate () EmployeeProfile profile ;

profile = (EmployeeProfile)profileReference . get () ;

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

removeEmployee (profile . getID ( » ;

/ / extricate ( )

} / / class LinkedLi s t / / c l a s s EmployeeCache

Теперь приведем классы EmployeeProfile И EmployeeID:

class EmployeeProfile (

private EmployeeID id;

/ / Идентификационный номер служащего .

private Locale locale ;

/ /

Языковые предпочтения .

private boolean supervisor;

 

 

private String пате ;

/ /

Имя

служащего .

public EmployeeProfile (EmployeeID id,

 

Locale locale ,

this . id = id;

boolean

supervisor,

string пате)

(

 

 

 

this . locale = locale ;

 

 

this. supervisor = supervisor;

 

this . naтe = пате ;

 

 

 

}/ / Cons tructor ( EmployeeI D, Locale, Ьооlеаn, String)

public EmployeeID getID ()

(

return

id; }

public Locale getLocale ()

(

return

locale ;

public boolean isSupervisor() { return supervisor;

} / / class EmployeeProfile

 

 

 

class EmployeeID (

 

 

 

private String id;

 

 

 

/ * *

 

 

 

*

Con structor

 

 

 

*

@param id Строка , содержащая идентификационный номер

*

служащего .

 

 

 

* /

 

 

 

public EmplоуееID (Striпg id)

(

 

 

this . id = id;

 

 

 

)

// constructor (String)

 

 

 

 

Cache Management 8 305

/ * *

 

* Возвращает значение хэшкода

этого объекта .

* /

 

public int ЬазЬСоОе ( ) { return

id. hashCode () ; }

/

* *

 

*

Возвращает true , если данный объект

представляет собой EmployeeID, равный этому .

*/

public boolean equals (Object obj ) { return ( obj instanceof EmployeeID

" id. equals « (EmployeeID) obj ) . id) ) ; } / / equa l s (Obj ect)

/* *

* Возвращает строковое представление этого Employee I D .

* / public String toString (} { return id; }

/ / clas s Employee I D

А теперь -- класс org . cl ickblocks dataStructure . CleanupQueue, кото­

.

рый используется классом EmployeeCache для управления высвобождением объектов EmployeeProfile программой сборки мусора.

/ * *

*

*

* *

*

Этот класс инкапсулирует ReferenceQueue .

Он гарантирует , что объекты Reference только одного вида

поставлены в очередь в объект ReferenceQueue

это объекты,

позволяющие объектам CleanupIF,

поставленным в

очередь

в объект ReferenceQueue, удалять

самих себя из любой

структуры данных, частью которой

они являются .

 

/

public class CleanupQueue

/ * *

*

Re ferenceQueue ,

которьм инкапсулирован

в этом объекте .

* /

 

 

 

private ReferenceQueue refQueue = new ReferenceQueue () ;

/ * *

 

 

 

*

Содержит t rue ,

если н е акончен вызов ,

активизирующий

*

очистку .

 

 

* /

 

 

 

private boolean cleaning;

306 Глава 7. Структурные шаблоны nроектироваНИR

/**

*

 

Возвращает

SoftReference ,

предназначенный для помещения

 

в очередь

ссыло , ин апсулированную в этом объе те .

*

 

 

 

 

*

 

@param obj

 

 

*

 

Объе т, на

оторый будет у азывать эта ссыл а .

*

 

@param cleanup

 

*

 

Объе т CleanupIF, метод extricate OTOpoгo должен

 

вызываться

после того, aK SoftReference помещен в очередь .

*/

 

 

 

public SoftReference createSoftReference (Object obj ,

 

 

CleanupIF cleanup)

(

 

return new SoftCleanupReference (Obj , refQueue , cleanup) ;

}

// createReference (Obj ect ,

CleanupIF)

/**

*

*

* */

Вызывает метод extricate всех объектов Cleanup I F, стоящих в очереди . Если в данный момент активизирован не оторый вызов , то просто выходим из метода .

public void cleanup ( )

 

synchronized

(this)

 

 

if

(cleaning)

 

 

 

return;

 

 

 

}

//

i f

 

 

 

cleaning = true ;

 

 

//

synchroni zed

 

try (

 

 

 

 

while

(refQueue . poll ( ) !=null)

 

 

 

SoftCleanupReference r;

 

 

 

r = (SoftCleanupReference) refQueue . remove ( ) ;

 

 

 

r . extricate ( ) ;

 

 

 

//

while

 

 

 

catch

(InterruptedException е) (

 

 

finally (

 

 

 

cleaning

false ;

//

//

//

t ry

 

cleanup ( )

 

class

CleanupQueue

 

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