Допустим есть интерфейс MyInterfaceV1, который используется в большом количестве существующего кода, и также большое количество классов, реализующих этот интерфейс. Мы хотим добавить к нему пару методов, для реализации некой новой функциональности. Но MyIntefaceV1 изменить нельзя.
Предлагается сделать следующее. Создается MyIntefaceV2, унаследованный от MyInterfaceV1:
class MyInterfaceV2 : public MyInterfaceV1
{
public:
// методы для новой функциональности ...
};
Новые классы реализуют оба интерфейса:
class ConcreteV2 : public MyInterfaceV2
{
public:
// MyInterfaceV1 methods
...
// MyInterfaceV2 methods
...
};
Потом они передаются в функции, ожидающие MyInterfaceV1:
Код, который не знает про MyIntefaceV2, продолжает использовать MyInterfaceV1 как и раньше. А код, который хочет задействовать MyInterfaceV2, делает следующее:
Здравствуйте, l33thaxor, Вы писали:
L>...
L>Вопрос: насколько хорошо такое решение через dynamic_cast?
В качестве "подставки" для какого-то особого случая — сойдет.
А как часть общего дизайна — не годится. Интерфейсы на то и интерфейсы,
чтобы их один раз спроектировать, а потом использовать, не меняя,
на протяжении долгого времени.
В COM (Windows) этот вопрос решили следующим образом — все классы наследуются от
абстрактного IUnknown, который имеет метод QueryInterface для запроса интерфейсов.
Получается, что любой объект может в рантайме узнавать, поддерживает ли другой
объект необходимую функциональность. Для этого, правда, пришлось вводить такие
вещи, как подсчет ссылок, глобальные идентификаторы (GUID), и др. Но объекты COM
обычно сами по себе довольно пухленькие, поэтому небольшой оверхед не заметен.
L>Есть ли другие варианты?
Здравствуйте, okman, Вы писали:
O>Здравствуйте, l33thaxor, Вы писали:
L>>...
L>>Вопрос: насколько хорошо такое решение через dynamic_cast?
O>В качестве "подставки" для какого-то особого случая — сойдет. O>А как часть общего дизайна — не годится. Интерфейсы на то и интерфейсы, O>чтобы их один раз спроектировать, а потом использовать, не меняя, O>на протяжении долгого времени.
Поэтому и спрашиваю про альтернативы.
O>В COM (Windows) этот вопрос решили следующим образом — все классы наследуются от O>абстрактного IUnknown, который имеет метод QueryInterface для запроса интерфейсов.
Не вижу принципиальной разницы между QueryInterface и dynamic_cast применительно для данной задачи.
L>>Есть ли другие варианты?
O>Такой, к примеру: O>
Это, конечно, фигня, а не вариант. SomeFunction не может сама создать объекты классов реализующих эти интерфейсы, потому как она не знает, какие классы их реализуют, и как их создавать.
Здравствуйте, l33thaxor, Вы писали:
L>Поэтому и спрашиваю про альтернативы.
Альтернатив, видимо, и нет.
Возможность расширения или хотя бы версионности нужно закладывать в протоколах
изначально, попытки сделать это в более поздних стадиях обречены.
O>>В COM (Windows) этот вопрос решили следующим образом — все классы наследуются от O>>абстрактного IUnknown, который имеет метод QueryInterface для запроса интерфейсов.
L>Не вижу принципиальной разницы между QueryInterface и dynamic_cast применительно для данной задачи.
QueryInterface маскирует получение целевого объекта — это может быть указатель this,
либо копия всего объекта, либо вообще какой-нибудь прокси. И время его жизни контролируется
подсчетом ссылок, что в некоторых ситуациях может устранить путаницу.
А RTTI связан с накладными расходами на ровном месте и вообще плохо переносим.
L>>>Есть ли другие варианты?
O>>Такой, к примеру: O>>...
L>Это, конечно, фигня, а не вариант. SomeFunction не может сама создать объекты классов реализующих эти интерфейсы, потому как она не знает, какие классы их реализуют, и как их создавать.
Это лишь сильно упрощенный пример. Классы могут создаваться фабриками, заворачиваться во
всякие обертки для обеспечения совместимости интерфейса, и так далее.
Хотя плохой дизайн это все равно не спасет.
Здравствуйте, l33thaxor, Вы писали: L>Допустим есть интерфейс MyInterfaceV1, который используется в большом количестве существующего кода, и также большое количество классов, реализующих этот интерфейс. Мы хотим добавить к нему пару методов, для реализации некой новой функциональности. Но MyIntefaceV1 изменить нельзя.
[...] L>Вопрос: насколько хорошо такое решение через dynamic_cast? Есть ли другие варианты?
Именно так и делают в стандартной библиотеке Java. См., например, LayoutManager/LayoutManager2. Кое-где, кажется, даже до 3-й версии дошли. Всё это выглядит, конечно, кривовато, но единственный способ исправить ошибки дизайна. И не так уж накладно на фоне остального.