
GrandM-Patterns_in_Java
.pdfCoche Monogement - 297
раскрываются при описании некоторых шаблонов в книге [Grand2001). Они
упоминаются в разделе «Шаблоны проектирования, связанные с шаблоном
Cache Management.) в конце описания данного шаблона.
Если невозможно добиться абсолютной согласованности чтения или записи, можно задать относительную согласованность. Она не гарантирует, что содер жимое кэша всегда будет совпадать с исходным источником объектов. Вместо этого она гарантирует, что при обновлении кэша или источника данных эти из
менения соответствующим образом будyr отражены в течение определенного промежутка времени. Более подробно этот подход рассматривается при описа нии шаблона Ephemeral Cache Item, которое можно найти в книге [Grand2001).
ПРИМЕР КОДА
Предположим, необходимо написать ПО ДЛЯ системы учета рабочего времени служащих. Система состоит из терминалов и сервера учета рабочего времени. Терминалы представляют собой небольшие устройства, вмонтированные в стену обфиса. Когда служащий приходит на работу или уходит с работы, он оповещает этом систему учета рабочего времени, проводя идентификационной карточ кой через терминал учета времени. Терминал считывает идентификационный номер служащего и подтверждает правильность карточки, показывая на экране имя сотрудника и варианты выбора. Затем служащий нажимает кнопку, чтобы
указать: он начинает работу, заканчивает работу, идет на перерыв или другие варианты. Терминалы передают эти события системы учета рабочего времени серверу учета рабочего времени. В конце каждого периода платежа система
платежных ведомостей фирмы получает от системы учета рабочего времени ко личество часов работы каждого служащего и подготавливает чеки на оплату.
Все детали информации, которую работник видит на экране, зависят от профиля
работника, данные которого терминал получает от сервера учета рабочего вре мени. Параметры служащего содержат его имя, язык, на котором выдается под
сказка, и специальные опции, которые могут быть связаны с этим служащим.
Большинство фирм закрепляют за своими служащими определенное рабочее
место. Такие служащие обычно используют ближайший к своему месту работы
терминал учета времени. Чтобы избежать очередей перед терминалами, рекомен дуется такое их расположение, чтобы один и тот же терминал учета времени ис
ПОльзовался менее чем 70 служащими, имеющими определенное рабочее место. |
|||||||||
З |
|
|
|
|
|
|
|
|
|
|
начит |
|
|
|
|||||
|
|
|
|
|
|
ельную часть стоимости системы учета рабочего времени составляет |
|||
СТ |
|
имость |
терминалов. Чтобы быть дешевыми, терминалы должны иметь па |
||||||
|
О |
|
|
|
|
||||
М |
|
|
ь |
минимальных размеров. Это означает, что придется задать довольно уме |
|||||
р |
ят |
|
|
||||||
е |
н |
ный макс;;rмальный размер кэша |
. |
С другой стороны, для поддержки высо |
|||||
Кой |
|
скорости реагирования желательно, чтобы терминалы сохраняли в кэше |
|||||||
параметры служащих и почти всегда немедленно реагировали на предоставлен |
|||||||||
ную |
идентификационную карту. При у |
||||||||
|
|
|
|
становлении первоначального максималь |
НОГО размера кэша за основу принимается рекомендация, согласно которой


|
• Глава 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; |

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) |
|
|
|

