developing.ru - клуб программистов Здесь может быть и ваша реклама.
developing.ru >технология COM >
Михаил Безверхов
aspid@developing.ru

Самозарождение невозможно. А самоубийство?

Итак, по результатам предшествующих тестов, у нас "всё работает". У нас теперь другая проблема, у нас - "не перестаёт работать". Мы обнаружили, что наша механика страдает тяжёлым недостатком - предложенный механизм создания объектов настолько обезличивает их, что их невозможно уничтожить по окончании их использования. Если сервер выдал клиенту ссылку на свой статический объект, то в этом нет никакой проблемы - по завершении сервера все объекты сервера будут уничтожены автоматически. Но если сервер создал экземпляр объекта динамически оператором new, то этот объект не может уничтожить никто. Его не может уничтожить клиент - в контексте клиента такой объект неизвестен и вызов delete на стороне клиента невозможен. Но его не может уничтожить и сервер... Подумайте, даже, если бы мы создали специальную функцию внутри сервера, которую бы вызывали для уничтожения такого объекта (совершенно аналогично подходу, который мы использовали для создания), то всё равно указатель на статический тип, который сервер использует, чтобы создать новый объект, не равен указателю на тот абстрактный тип, который сервер возвращает клиенту. Между ними стоит операция приведения типа, которая, в общем случае, даже численное значение указателя изменяет, не говоря про его семантику.

Поэтому, когда клиент передаст такой указатель (а другого указателя на стороне клиента нет) обратно серверу, то и сам сервер не сможет разобраться - приведение типов указателя необратимо. Поэтому и на стороне сервера операция delete так же бессмысленна. Это - обратная сторона всё той же проблемы, которую мы рассматривали в статье "Клиенту - клиентово, а серверу - серверово".

Однако, если наша технология претендует на право на жизнь, то эту проблему в числе первых решить нужно... Как?

Здесь самое время вспомнить, что хотя ссылка на объект и выдаётся клиенту, хотя клиент обращается по этой преобразованной ссылке абсолютно правильно, всё равно объект-то создаётся сервером и существует в контексте сервера. Иными словами - хотя выдаваемый адрес преобразуется, первоначальный адрес от этого существовать не перестаёт. И пусть сам модуль сервера его может специально нигде и не запоминать, но объект-то должен знать свой указатель this? Объект помнит свой указатель this, объект существует в контексте сервера... по-моему, этого вполне достаточно, чтобы объект мог сам вызвать в отношении себя оператор delete. Вот так - delete this. И вызов этот будет вполне корректен - сама операция удаления объекта выполняется не объектом (код delete объекту не принадлежит), объектом она только вызывается. Образно говоря, как "последнее издыхание" объект вполне может вызвать операцию самоуничтожения и "ничего ему за это не будет".

Проблема сразу переходит в иную плоскость - если понятно "что", то теперь нужно придумать "как". Как дать понять объекту, что его услуги больше не нужны? Самый простой путь - предусмотреть в составе объекта специальный метод. Это будет самый обычный метод, который будет обязан реализовывать любой объект и экспонировать его любому клиенту. Когда клиент завершит свои операции с объектом он вызовет этот метод и... А когда клиент завершит свои операции с объектом?

Я имею в виду вот что. Операция изготовления объекта действительно выполняется за время жизни объекта ровно один раз. И операция его уничтожения - тоже. Но программа может получить ссылку на объект в переменную "a", потом присвоить её переменной "b", потом передать ещё куда-нибудь... Так когда клиент закончит свои операции с объектом? Когда это будет очевидно по смыслу (семантике) выполняемых операций или когда все эти переменные перестанут существовать? Мне кажется, что второе - надёжнее. Ведь компилятор-то гораздо точнее программиста может помнить, когда та или иная переменная выходит за область видимости, т.е. становится недоступной и разрушается. Только вот получается, что теперь и клиент должен как-то уведомлять сервер о том, что происходит внутри клиента с переданной ему ссылкой на объект? Самопроизвольно-то сервер этого узнать не в состоянии.

Да. Решая одну проблему получили две - и метод самоуничтожения надо сделать и метод уведомления надо сделать... А нельзя ли обе этих задачи совместить? Этим бы мы сэкономили метод, упростили бы клиентский код, уменьшили бы вероятность ошибок. И эта задача только кажется парадоксальной, на самом деле - совместить можно. Ведь в "уведомлении об использовании", которое клиент должен посылать серверу сервер совершенно не интересует где и как клиентом используется ссылка. Сервер интересует только факт - "ссылка используется". В таком случае сервер должен только вести простой счётчик ссылок - как только придёт сигнал "ссылка используется" сервер прибавит туда единицу, как только придет сигнал "ссылка перестала использоваться" сервер вычтет оттуда единицу... а как только счётчик ссылок достигнет нуля это как раз и будет означать, что объект-то больше никому и не нужен - если все ссылки на него потеряны, то как кто-нибудь сможет к этому объекту обратиться? И тогда сервер должен уничтожить такой объект. Поскольку мы ранее выяснили, что delete this - вполне корректная операция, то и ведение счётчика ссылок вполне можно доверить самому объекту. А клиенту - вменить в обязанность всякий раз уведомлять объект о том, что клиент размножил число ссылок на этот объект или, напротив, сократил. 

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

Итак, будем считать, что одну проблему мы решили - всякий объект должен иметь в своем составе либо специальный метод, либо пару методов, вызывая которые клиент изменял бы счетчик ссылок объекта соответственно числу фактических экземпляров указателя на клиентской стороне. Одна лишняя операция при присваивании, зато потом - никаких проблем.

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

Эта проблема - посложнее первой. Посложнее тем, что хоть мы и говорим "объект должен иметь специальный метод, изменяющий счетчик ссылок" мы тем не менее отчётливо понимаем, что иным способом, как в составе интерфейса экспонировать такой метод клиенту невозможно. Выходит, что либо такой метод располагается в составе одного, специального интерфейса, который (интерфейс) будет обязан экспонировать любой объект, либо такой метод должен присутствовать в составе любого интерфейса, экспонируемого объектом. 

Кроме того, мы сталкиваемся и с совершенно особой проблемой - допустим, мы точно знаем, что у объекта X реализуются два интерфейса Y и Z. Когда мы создаём такой объект, то сервер по нашему желанию в состоянии привести указатель на статический тип к любому абстрактному - либо Y, либо Z. И выдать его нам. А что делать, если мы уже имеем указатель на Y, а нам понадобился Z того же самого объекта? Имели бы сам первоначальный указатель - привели бы легко. Но один абстрактный тип непонятно каким (на стороне клиента) образом не приводится к другому. Такую операцию тоже должен выполнять сервер. Скорее даже - по тем же причинам, по которым мы делегируем ведение счётчика ссылок объекту, - сам объект этого сервера. 

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

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

предыдущий выпускархив и оглавлениеследующий выпуск
Авторские права © 2001 - 2004, Михаил Безверхов
Публикация требует разрешения автора

разделы сайта

форум

технология COM

оптимизация сайтов


© 2000-2004 Клуб программистов developing.ru