Сущность технологии СОМ. Библиотека программиста | страница 30
class IFastString {
public:
// faux version 1.0
// фиктивная версия 1.0
virtual void Delete(void) = 0;
virtual int Length(void) = 0;
virtual int Find(const char *psz) = 0;
// faux version 2.0
// фиктивная версия 2.0
virtual int FindN(const char *psz, int n) = 0;
};
Это решение почти работает. Те клиенты, у которых оттранслирована исходная версия интерфейса, остаются в счастливом неведении относительно всех составляющих таблицы vtbl, кроме первых трех. Когда старые клиенты получают обновленные объекты, имеющие в vtbl вход для FindN, они продолжают нормально работать. Проблема возникает, когда новым клиентам, ожидающим, что IFastString имеет четыре метода, случится столкнуться с устаревшими объектами, где метод FindN не реализуется. Когда клиент вызовет FindN на объект, странслированный с исходным описанием интерфейса, результаты будут вполне определенными. Программа прервет работу.
В этой методике проблема заключается в том, что она нарушает инкапсуляцию объекта, изменяя открытый интерфейс. Подобно тому, как изменение открытого интерфейса в классе C++ может вызвать ошибки на этапе трансляции, когда происходит перестройка клиентского кода, так и изменение двоичного описания интерфейса вызовет ошибки на этапе выполнения, когда клиентская программа перезапущена. Это означает, что интерфейсы должны быть неизменяемыми с момента первой редакции. Решение этой проблемы заключается в том, чтобы разрешить классу реализации выставлять более чем один интерфейс. Этого можно достигнуть, если предусмотреть, что один интерфейс порождается от другого, связанного с ним интерфейса. А можно сделать так, чтобы класс реализации наследовал от нескольких несвязанных классов интерфейса. В любом случае клиент мог бы использовать имеющуюся в C++ возможность определения типа на этапе выполнения – идентификацию Runtime Type Identification – RTTI, чтобы динамически опросить объект и убедиться в том, что его требуемая функциональность действительно поддерживается уже работающим объектом.
Рассмотрим простой случай интерфейса, расширяющего другой интерфейс. Чтобы добавить в IFastString операцию FindN, позволяющую находить n–е вхождение подстроки, необходимо породить второй интерфейс от IFastString и добавить в него новое описание метода:
class IFastString2 : public IFastString {
public: // real version 2.0
// настоящая версия 2.0
virtual int FindN(const char *psz, int n) = 0;
};
Клиенты могут с уверенностью динамически опрашивать объект с помощью оператора C++ dynamic_cast, чтобы определить, является ли он совместимым с IFastString2