Вопрос по дизайну
От: l33thaxor  
Дата: 20.07.11 20:32
Оценка:
Допустим есть интерфейс MyInterfaceV1, который используется в большом количестве существующего кода, и также большое количество классов, реализующих этот интерфейс. Мы хотим добавить к нему пару методов, для реализации некой новой функциональности. Но MyIntefaceV1 изменить нельзя.

Предлагается сделать следующее. Создается MyIntefaceV2, унаследованный от MyInterfaceV1:
class MyInterfaceV2 : public MyInterfaceV1
{
public:
    // методы для новой функциональности ...
};


Новые классы реализуют оба интерфейса:
class ConcreteV2 : public MyInterfaceV2
{
public:
    // MyInterfaceV1 methods
    ...
    // MyInterfaceV2 methods
    ...
};


Потом они передаются в функции, ожидающие MyInterfaceV1:
void foo(MyIntefaceV1 * obj) { ... }

ConcreteV2 * obj = new ConcreteV2();
foo(obj);


Код, который не знает про MyIntefaceV2, продолжает использовать MyInterfaceV1 как и раньше. А код, который хочет задействовать MyInterfaceV2, делает следующее:

void bar(MyInterfaceV1 * obj)
{
    if (MyInterfaceV2 * objv2 = dynamic_cast<MyInterfaceV2 *>(obj))
    {
        // используем MyInterfaceV2
    }
}


Вопрос: насколько хорошо такое решение через dynamic_cast? Есть ли другие варианты?
Re: Вопрос по дизайну
От: okman Беларусь https://searchinform.ru/
Дата: 21.07.11 06:30
Оценка:
Здравствуйте, l33thaxor, Вы писали:

L>...


L>Вопрос: насколько хорошо такое решение через dynamic_cast?


В качестве "подставки" для какого-то особого случая — сойдет.
А как часть общего дизайна — не годится. Интерфейсы на то и интерфейсы,
чтобы их один раз спроектировать, а потом использовать, не меняя,
на протяжении долгого времени.

В COM (Windows) этот вопрос решили следующим образом — все классы наследуются от
абстрактного IUnknown, который имеет метод QueryInterface для запроса интерфейсов.
Получается, что любой объект может в рантайме узнавать, поддерживает ли другой
объект необходимую функциональность. Для этого, правда, пришлось вводить такие
вещи, как подсчет ссылок, глобальные идентификаторы (GUID), и др. Но объекты COM
обычно сами по себе довольно пухленькие, поэтому небольшой оверхед не заметен.

L>Есть ли другие варианты?


Такой, к примеру:
void SomeFunction(IFaceOld *pConcreteOld)
{
    IFaceNew *pConcreteNew = new IFaceNew(pConcreteOld);

    ...

    delete pConcreteNew;
}
Re[2]: Вопрос по дизайну
От: l33thaxor  
Дата: 25.07.11 03:12
Оценка:
Здравствуйте, okman, Вы писали:

O>Здравствуйте, l33thaxor, Вы писали:


L>>...


L>>Вопрос: насколько хорошо такое решение через dynamic_cast?


O>В качестве "подставки" для какого-то особого случая — сойдет.

O>А как часть общего дизайна — не годится. Интерфейсы на то и интерфейсы,
O>чтобы их один раз спроектировать, а потом использовать, не меняя,
O>на протяжении долгого времени.

Поэтому и спрашиваю про альтернативы.

O>В COM (Windows) этот вопрос решили следующим образом — все классы наследуются от

O>абстрактного IUnknown, который имеет метод QueryInterface для запроса интерфейсов.

Не вижу принципиальной разницы между QueryInterface и dynamic_cast применительно для данной задачи.

L>>Есть ли другие варианты?


O>Такой, к примеру:

O>
O>void SomeFunction(IFaceOld *pConcreteOld)
O>{
O>    IFaceNew *pConcreteNew = new IFaceNew(pConcreteOld);

O>    ...

O>    delete pConcreteNew;
O>}
O>


Это, конечно, фигня, а не вариант. SomeFunction не может сама создать объекты классов реализующих эти интерфейсы, потому как она не знает, какие классы их реализуют, и как их создавать.
Re[3]: Вопрос по дизайну
От: okman Беларусь https://searchinform.ru/
Дата: 25.07.11 07:14
Оценка:
Здравствуйте, l33thaxor, Вы писали:

L>Поэтому и спрашиваю про альтернативы.


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

O>>В COM (Windows) этот вопрос решили следующим образом — все классы наследуются от

O>>абстрактного IUnknown, который имеет метод QueryInterface для запроса интерфейсов.

L>Не вижу принципиальной разницы между QueryInterface и dynamic_cast применительно для данной задачи.


QueryInterface маскирует получение целевого объекта — это может быть указатель this,
либо копия всего объекта, либо вообще какой-нибудь прокси. И время его жизни контролируется
подсчетом ссылок, что в некоторых ситуациях может устранить путаницу.
А RTTI связан с накладными расходами на ровном месте и вообще плохо переносим.

L>>>Есть ли другие варианты?


O>>Такой, к примеру:

O>>...

L>Это, конечно, фигня, а не вариант. SomeFunction не может сама создать объекты классов реализующих эти интерфейсы, потому как она не знает, какие классы их реализуют, и как их создавать.


Это лишь сильно упрощенный пример. Классы могут создаваться фабриками, заворачиваться во
всякие обертки для обеспечения совместимости интерфейса, и так далее.
Хотя плохой дизайн это все равно не спасет.
Re: Вопрос по дизайну
От: gegMOPO4  
Дата: 25.07.11 09:04
Оценка:
Здравствуйте, l33thaxor, Вы писали:
L>Допустим есть интерфейс MyInterfaceV1, который используется в большом количестве существующего кода, и также большое количество классов, реализующих этот интерфейс. Мы хотим добавить к нему пару методов, для реализации некой новой функциональности. Но MyIntefaceV1 изменить нельзя.
[...]
L>Вопрос: насколько хорошо такое решение через dynamic_cast? Есть ли другие варианты?

Именно так и делают в стандартной библиотеке Java. См., например, LayoutManager/LayoutManager2. Кое-где, кажется, даже до 3-й версии дошли. Всё это выглядит, конечно, кривовато, но единственный способ исправить ошибки дизайна. И не так уж накладно на фоне остального.
Re: Вопрос по дизайну
От: jerry_ru  
Дата: 25.07.11 18:31
Оценка:
Здравствуйте, l33thaxor, Вы писали:

L>Вопрос: насколько хорошо такое решение через dynamic_cast? Есть ли другие варианты?


dynamic_cast плохо переносим между различными модулями.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.