понедельник, ноября 13, 2006

Ловкость рук, и ни какого мошенства

Задача: дан класс имеющий приватный TThreadList (список с thread-safe доступом) и содержащий некие объекты. Нужно написать метод предоставляющий безопасный доступ к содержимому списка, без доступа пользователя к объекту-списку.

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

Решение: Определяем интерфейс IObjectProvider, который будет возвращать наш метод.

Type

IObjectProvider = Interface

Function GetObject : TObject;

Property Object : TObject Read GetObject;

End;

Затем описываем класс TObjectProvider, который реализует описанный ранее интефейс.

Type

//
TObjectProvider = Class(TInterfacedObject, IObjectProvider)

Private

FList : TThreadList;
FObject : TObject;

Public

Constructor Create(Const AList : TThreadList; AIndex : Integer);
Destructor Destroy; Override;

Function GetObject : TObject;

End;
//

// Реализация конструктора
Begin

FList := AList;
FObject := TObject(FList.LockList.Items[AIndex]);

End;
//

// Реализация деструктора
Begin

FList.UnlockList;
Inherited;

End;
//

// Реализация метода GetObject
Begin

Result := FObject;

End;
//


Теперь можно реализовать и наш метод:

Function SafeGetObject(AIndex : Integer) : IObjectProvider;
Begin

Result := TObjectProvider.Create(ThreadSafeList, AIndex);

End;

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


Пример:

With SafeGetObject(5) Do
Begin

Object.Method;
...
Object.Method;

End;

4 комментария:

r3code комментирует...

А для какой задачи протребовалось это?

Kazantsev Alexey комментирует...

Это потребовалось для поддержки перечисления циклом for-in-do задач находящихся в пуле потоков. По подобной схеме был реализован перечислитель блокирующий список перед перечислением и снимающий блокировку после.

Ruzzz комментирует...

Я бы назвал не Provider а Holder )
Было бы интересно если бы Provider создавался на стеке.

Kazantsev Alexey комментирует...

>Я бы назвал не Provider а Holder
По смыслу это именно провайдер.

>Было бы интересно если бы Provider создавался на стеке
Со стеком будут проблемы, а вот избавиться от создания объекта-провайдера в общем-то можно. И даже, как мне видится, двумя способами.