Как реализовать коллекцию объектов и объект-посредник?
От: michus Россия  
Дата: 17.08.05 14:29
Оценка:
Имеем коллекцию объектов, которая для внешнего мира выглядит, например так:
interface IChild {
  [propget, id (1)] HRESULT Attr ([out, retval] LONG * pVal);
  [propput, id (1)] HRESULT Attr ([in] LONG newVal);
};

interface IChildCollection {
  [id (1)] HRESULT Add ([in] LONG data, [out, retval] LONG * pIndex);
  [id (2)] HRESULT Del ([in] LONG nIndex);
  [propget, id (3)] HRESULT Items ([in] LONG nIndex, [out, retval] IChild ** pVal);
  [propput, id (3)] HRESULT Items ([in] LONG nIndex, [in] IChild * newVal);
  [propget, id (4)] HRESULT Count ([out, retval] LONG * pVal);
};

В VB.NET клиенте надо уметь следующее:
dim IChildCollection Coll as new ChildCollectionClass
Coll.Add (1)
Coll.Add (2)

' 1. Сохраняем ссылку на первый элемент коллекции.
dim IChild c
set c = Coll.Items [1]

' 2. Изменяем атрибут первого элемента коллекции (через временный объект CChild, созданный и удалённый тут же).
Coll.Items [1].Attr = 1
if Coll.Items [1].Attr = 1 then
  ' Надо быть здесь
end if
if c.Attr = 1 then
  ' Надо быть здесь
end if

' 3. Изменяем атрибут первого элемента коллекции через сохранённую ссылку.
c.Attr = 2
if Coll.Items [1].Attr == 2 then
  ' Надо быть здесь
end if

' 4. Удаляем первый элемент коллекции.
Coll.Del (1)

' 5. Изменяем атрибут первого элемента коллекции (через временный объект CChild, созданный и удалённый тут же).
Coll.Items [1].Attr = 10

' 6. Проверяем атрибут элемента по сохранённой ссылке.
'    Т.к. элемент был удалён из коллекции, то значение не изменилось (как было в п. 2).
if c.Attr == 1 then
  ' Надо быть здесь
end if

Есть простой способ реализации подобной функционатьности — хранение готовых COM объектов в коллекции и возвращение их в get_Items (LONG nIndex, IChild ** pVal).
Однако в таком случае имеем большие накладные расходы при создании коллекции из массива сырых данных (напр. из файла), да и работа через COM интерфейсы не всегда удачна (исчезают понятия константных методов, нет возможности убрать "лишние" проверки передаваемых в методы параметров из-за необходимости ориентироваться на "дурака" при открытых миру интерфейсах и т.п.).

Более сложный способ — хранить данные в коллекции в удобном для обработки виде, а при запросе из клиента — создавать COM объект-посредник.

Возникает вопрос:
Какой должен быть механизм взамиодействия объекта-посредника с коллекцией, да и когда создавать/удалять его, чтобы поддерживать заданную функциональность и не было бы больших накладных расходов при выполнении п.4., когда необходимо будет произвести отсоединение недействительных посредников от коллекции с оставлением им ранее находившихся в элементе коллекции данных ?

На ум приходят 2 варианта реализации:

Вариант 1. Вариация на тему хранения готовых COM объектов. Каждый элемент коллекции динамически размещается в куче и имеет счётчик ссылок для управления временем жизни. Коллекция хранит указатели на свои элементы. Объект-посредник хранит только указатель на данные, но не забывает управлять счётчиком ссылок. При запросе из клиента создаваемый объект-посредник получает адрес элемента коллекции и увеличивает у него счётчик ссылок для предотвращения освобождения памяти при удалении элемента из коллекции.

Вариант 2. Элемент коллекции представляет собой пару: [Даные]/[умный указатель на Объект-посредник]. При запросе из клиента мы создаём и запоминаем объект-посредник для элемента (если он не был создан ранее). При удалении элемента из коллекции необходимо будет удостовериться в удержании его объекта-посредника клиентом (Достаточно ли для этого сравнить m_dwRef с 1 ?). Если объект-посредник удерживается клиентом, то передать в объект-посредник динамически размещённую в памяти копию данных и забыть о посреднике, обнулив умный указатель. Т.о. объект-посредник должен иметь флаг владения данными, при выставлении которого он самостоятельно удаляет их из памяти. Как экстремальная разновидность данного варианта — обнулять указатель в объекте-посреднике, что приведёт к неработоспособности удерживаемых клиентом "ссылок" (они всегда будут ругаться E_UNEXPECTED).

Достоинства/недостатки:
— В1 Необходимо динамически размещать все элементы коллекции по отдельности.
+ В1 Простота реализации, сравнимая с хранением "готовых" COM объектов.
+ В1 Не надо заботиться о времени жизни объекта-посредника: Клиент запросил его — создали и забыли.
— В2 Необходимо корректировать указатели объектов-посредников при добавлении/удалении объектов из коллекции.
— В2 Реализация более сложная.
+ В2 Быстрое выделение памяти под большие массивы данных и значительно большая скорость работы при отсутствии объектов-посредников или при их небольшом количестве.

Расширим функциональность коллекции:
interface IChildCollection {
  ...
  [propget, id (5)] HRESULT MinAttr ([out, retval] LONG * pVal);
  [propget, id (6)] HRESULT MaxAttr ([out, retval] LONG * pVal);
};

Теперь коллекция должна выдавать статистическую информацию о своих элементах. Как сделать так, чтобы не было необходимости пересчитывать её при каждом запросе ?

Вариант А. Наплевать на требования и вычислять статистику каждый раз.
Вариант Б. Подписываться на события от объектов-посредников и подправлять статистику (выставлять флаг необходимости пересчёта) при изменении данных через них.

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

ВА. Совместим с обеими реализациями, но самый медленный.
ВБ + В1. Возникают трудности отписания от событий при удалении элементов из коллекции, когда клиент не освободил объект-посредник. Как вариант можно вообще не отписываться от событий и только выставлять флаг пересчёта, но ни в коем случае не подправлять статистику, т.к. в ней могут появиться "левые" данные.
ВБ + В2. Отписаться при удалении объекта вполне реально, т.к. объект-посредник находится "под рукой".

Для решения проблемы ВБ + В1 появляется ещё один вариант:

Вариант 3. Каждый элемент коллекции динамически размещается в куче и имеет счётчик ссылок для управления временем жизни. Коллекция хранит пары: [Указатель на Даные]/[умный указатель на Объект-посредник]. Объект-посредник хранит только указатель на данные, но не забывает управлять счётчиком ссылок. При запросе из клиента объект-посредник создаётся только если его ещё не было, получает адрес элемента коллекции и увеличивает у него счётчик ссылок для предотвращения освобождения памяти при удалении элемента из коллекции.

Итак, если я Вас ещё не утомил: Кто какие варианты использовал, и в чём еще достоинства/недостатки приведённых вариантов ?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.