LINUX.ORG.RU

Вопрос по архитектуре

 ,


0

3

Допустим есть класс IF. Он умеет создавать IA и IB. (то что с префиксом I это интерфейс). Конкретные классы это F, A, B соответственно.

Еще в IF есть какой-то метод foo(IA *a). Внутри он преобразуется static_cast-ом в A и у него вызывается какой-то метод. Этого метода нет в интерфейсе IA, он намеренно скрыт и используется только внутри класса F в реализации.

Суть вопроса. static_cast в данном контексте зло, т.к. программист может определить класс AA, который наследует интерфейс IA, получается когда мы у IF вызовем foo и подсунем ему этот левый класс, то static_cast с-фэйлится. Как лучше всего избежать такой ситуации? Хранить enum с типом как то не хочется.

★★★

Последнее исправление: cetjs2 (всего исправлений: 1)

Хранить enum с типом как то не хочется

Корень проблемы недоязычков :)

arturpub ★★
()

по моему мнению проблема кроется в этом:

Этого метода нет в интерфейсе IA, он намеренно скрыт

anonymous
()

он намеренно скрыт

Чем скрыт?

bhfq ★★★★★
()
#include <stdio.h>
struct IA  { virtual ~IA() {} };
struct IAF {
  virtual ~IAF() {}
  virtual void bar() = 0;
};
struct A: public IA, public IAF {
  void test() { printf("A\n"); }
  void bar() { printf("AF\n"); }
};
struct IF {
  virtual ~IF() {}
  virtual void foo(IAF* a) = 0;
};
struct F: public IF {
  void foo(IAF* a) { a->bar(); }
};
int main(){
  F f; A a;
  a.test();
  f.foo(&a);
  return 0;
}
anonymous
()
Ответ на: комментарий от anonymous

в том то и суть, чтобы IF не знал про IAF. Мне максимально нужно скрыть внутренности реализации. А наружу что бы вылазил интерфейсик, суть проблемы в том, что можно подсунуть левый самопальный класс...

pozitiffcat ★★★
() автор топика
class IA
{
public:
    virtual ~IA() {}

    virtual void foo() = 0;
    virtual void bar() = 0;

protected:
    virtual void implementationDetailsThatAreIntentionallyHiddenFromPublic() {}
};
ilammy ★★★
()
Последнее исправление: ilammy (всего исправлений: 1)
Ответ на: комментарий от ilammy

И френд ещё. В общем, ты понел: паттерн «шаблонный метод».

ilammy ★★★
()
Ответ на: комментарий от ilammy

там bar может принимать параметр, какой нибудь указатель на C, про который интерфейс не должен знать, это особенности внутренней реализации..

френд это все костыли...

Если б это был просто метод я бы вообще не парился )))

pozitiffcat ★★★
() автор топика
Последнее исправление: pozitiffcat (всего исправлений: 2)
Ответ на: комментарий от pozitiffcat

френд это все костыли...

забавно это читать в таком треде

Stil ★★★★★
()
Ответ на: комментарий от pozitiffcat

придется извращаться:

struct IBar { virtual void bar(IA *a) = 0; virtual ~IBar() {} };

struct ABar : public IBar{
    void bar(IA *a){
        static_cast<A*>(a)->bar();
    }
}

.....

void IF::setBarExecuter(IBar *bar, int type){
........
}

// какое-то место где надо заюзаьт бар
auto barExecuter = getBarExecuter(a->getType());
if (barExecuter)
    barExecuter->bar(a);

pozitiffcat ★★★
() автор топика
Ответ на: комментарий от pozitiffcat

Да-да, я уже понял, что это не сработает. Теперь думаю чуть дольше одной минуты.

И что-то я вижу такое:

  1. A обязан знать про существование F, чтобы реализовать то, что ожидает F;
  2. F обязан знать про существование A, чтобы вызывать foo() только для А;
  3. F2 может не хотеть делать foo() для A;
  4. чтобы это оставалось в рамках интерфейсов без кастов вслепую, все должны знать о существовании этого foo(), поведение которого зависит от реальных типов, скрытых за IF и IA.
  5. foo() для A и F делает, что ему надо; для остальных — ничего.

Так что по идее правильным решением будет реализация double dispatch. То есть или enum, или dynamic_cast, или добавлять ещё по методу в IF и IA.

ilammy ★★★
()
Ответ на: комментарий от ilammy

яркий пример. функция bar требует какой нибудь IDirectx11Device*. Я не хочу что бы интерфейс знал про этот класс, иначе юзеру придется качать SDK. Я делаю аналогичное но с openGL и хочу что бы юзер ничего не знал про GLuint хендлы, и что их можно биндить. Это использует отрисовщик, который скрыт во внутренней реализации.

Да придется мутить с enum-ом.

pozitiffcat ★★★
() автор топика
Последнее исправление: pozitiffcat (всего исправлений: 1)

почему метод не должен быть в IA? а если действительно не должен, то зачем его вызывать у объекта? а если нужно вызывать, то зачем давать клиенту параметризировать именно объектом IA, а не A?

anonymous
()
Ответ на: комментарий от pozitiffcat

Не надо енумов, сделай нормально все, а не через пятую точку.

anonymous
()
Ответ на: комментарий от pozitiffcat

завели иерархию девайсов.

anonymous
()

Если для IA нельзя сделать свою реализацию, то какой же это интерфейс? Так что скрытый метод надо перенести в IA в секцию protected, и спокойно вызывать в IF, зафрендив его.

quiet_readonly ★★★★
()
Ответ на: комментарий от quiet_readonly

темболее вызываться метод будет не в IF, а F и зафрендить не получится, т.к. IA ничего не должен знать о F

pozitiffcat ★★★
() автор топика
Ответ на: комментарий от pozitiffcat

Да ничего не придеться там инклудить. И френд тоже не нужен.

anonymous
()

Еще в IF есть какой-то метод foo(IA *a). Внутри он преобразуется static_cast-ом в A и у него вызывается какой-то метод. Этого метода нет в интерфейсе IA, он намеренно скрыт и используется только внутри класса F в реализации.

Это извращение;-)

И F и A знают что то, что не должен знать никто снаружи, при этом A должен передаваться через эту самую наружу, но не должен выглядеть как A (потому что его там не видно)?

Либо делайте это хозяйство виртуальным и юзайте RTTI (dynamic_cast), либо заводите например таблицу видную тока A и F, кладите туда пойнтер на A и передавайте id таблицы. Есть еще много всяких извратных решений наверное, но:

программист может определить класс AA, который наследует интерфейс IA

«Можно придумать защиту от дурака, но только от неизобретательного»(ц)

AIv ★★★★★
()

почему метод не foo(A *)?

anonymous
()

Может стоит немного изучить язык, которым пользуешься? Для передачи A* клиенту не нужно знать A, достаточно forward declaration. Так что IA тут не нужен

anonymous
()
Ответ на: комментарий от pozitiffcat

У тебя параллельная иерархия. Не гуд.

anonymous
()
Ответ на: комментарий от anonymous

Блина... и кстати да, а я туплю!;-(((

Для передачи A* клиенту не нужно знать A, достаточно forward declaration

Самый годный коммент во всей ветке;-)

AIv ★★★★★
()
Ответ на: комментарий от AIv

где же это годный коммент? Ну представь что IF принимает foo(A *a), A = forward declaration. этот самый IF может конструировать IA, так как же мы IA передадим в метод foo?


// IA нихера не должен знать про детали типа hiddenMethod
struct IA{
    virtual void setSomething(int i) = 0;
    virtual ~IA() {}
}

struct IF{
    virtual IA *makeA() const = 0;
    virtual void drawA(IA *a) = 0;
    virtual ~IF() {}
}

struct ConcreteA : public IA{
    virtual void setSomething(int i) { ... }
    void hiddenMethod(SomeHidden *sh) { ... }
}

struct ConcreteF : public IF{
    ConcreteF() { m_sh = new SomeHidden(); }
    virtual IA *makeA() const { return new ConcreteA(); }
    virtual void drawA(IA *a) { 
        static_cast<ConcreteA*>(a)->hiddenMethod(m_sh); 
        // правильно избавится от static_cast
        // юзер может определить свой наследованный от IA класс
        // и попытаться запихать его в этот метод
    }
    SomeHidden *m_sh;
}

int main(){
    IF *f = new ConcreteF();
    IA *a = f->makeA();
    a->setSomething(100500);
    f->drawA(a);
    delete a;
    delete f;
    return 0;
}
pozitiffcat ★★★
() автор топика
Последнее исправление: pozitiffcat (всего исправлений: 1)
Ответ на: комментарий от pozitiffcat

1-3х тактов жалко? А танцы с бубеном вокруг енума или его аналогов думаете быстрее будут?

AIv ★★★★★
()
Ответ на: комментарий от pozitiffcat

«Доктор, вы уж определитесь, или туда, или сюда»(ц)

Если хочется сделать красиво - есть динамик_каст, это самое прямое. Если хочется сделать быстро - или статик_каст, или завести скажем в IA поле A* Aptr и его инициализировать правильно. А если хочется и быстро и красиво, да еще с такой архитектурой, так меняйте ЯП;-)

AIv ★★★★★
()
Ответ на: комментарий от AIv

Страуструп, против этого.. )) так и придется костылять с каким нибудь dynamic_cast-ом или заранее оговорить что левоту пихать не надо )))

pozitiffcat ★★★
() автор топика

Суть вопроса. static_cast в данном контексте зло,

Делай в отладочной версии dynamic_cast с проверкой успешности приведения, а в релизной версии static_cast, и будет тебе спокойно на душе.

pathfinder ★★★★
()
Ответ на: комментарий от AIv

А если хочется и быстро и красиво, да еще с такой архитектурой, так меняйте ЯП;-)

Осмелюсь спросить, это какой язык обеспечит такую же скорость и сохранит красоту?

pathfinder ★★★★
()
Ответ на: комментарий от pathfinder

Да какой нить кодогенератор на каком нить лиспе наверное;-)))

Знал бы - уже бы сам поменял;-(

AIv ★★★★★
()
Ответ на: комментарий от pathfinder

ммм. классное решение кстати, хоть и все равно костыли с приведением типов, но мне нравится, спасибо

pozitiffcat ★★★
() автор топика
Ответ на: комментарий от pozitiffcat

Ты неправильно спроектировал систему и мучаешься теперь. Если интерфейсы не получается использовать как интерфейсы, значит ты ошибся. Можно пример не абстрактный?

anonymous
()
Ответ на: комментарий от pozitiffcat

Твой ConcreteF не является IF, так как не умеет draw'ить IA.

anonymous
()

Я не знаю, что там у тебя за конкретные задачи, но скорее всего тебе нужен паттерн «мост» + pimpl.

anonymous
()

man dynamic_cast /thread

и вообще

Внутри он преобразуется static_cast-ом в A и у него вызывается какой-то метод. Этого метода нет в интерфейсе IA, он намеренно скрыт и используется только внутри класса F в реализации.

Костыли вы мои костыли, дайте я вас сейчас расцелую.
КостыльнЫе мои костыли, костыли костыли костыли костыли.

Или ещё вот тема есть:

Ехал Грека через реку,
Видит Грека в реке костыль.
Сунул Грека в реку костыль,
Костыль костыль костыль костыль.

nanoolinux ★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.