Очередной наивный вопрос по архитектуре
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 13.03.24 20:46
Оценка:
Здравствуйте!

Вопрос опять по архитектуре, но так как всё про плюсы, напишу сюда.

Вот у меня есть какая-то своя библиотека контролов.

У контролов есть два основных аспекта: реакция на пользовательский ввод и отрисовка. Реакцию на пользовательский ввод я пока выделять особо не хочу, пусть будет зашита, а вот отрисовку — хочу выделить отдельно.

Цель простая — дать пользователю возможность создавать свои "темы", без необходимости ковыряться в кишках либы или делать своих наследников и переопределять им методы рисования.

Во всех случаях у нас есть какая-то IControlPainterFactory, которая один раз в начале задаётся. А вот далее возникают вопросы, как лучше сделать

Какие я вижу альтернативы.

1) У нас есть
IControlPainter IControlPainterFactory::getPainter(IControl *pControl)

Там dynamic_cast'ами определять, для какого конкретно типа вызван getPainter, и возвращать соответствующую реализацию.

Или, можно сделать
enum ControlType {PushButton, RadioButton, CheckBox, ListView, TrieView, ...}

и
IControlPainter IControlPainterFactory::getPainter(ControlType controlType)


Это вроде как будет чутка пошустрее и более бинарно совместимо, если я захожу рисовальщик темы выделить в отдельную DLL, и собрать её другим компилятором.

Вопрос такой — как быть в том случае, если пользователь создал свой тип контрола, и сопоставил ему какой-то свой ControlType, дополняющий исходное определение? Тут будут грязные касты, насколько это фу? И, кстати, а как поведут себя плюсовые компиляторы различных стандартов, если будет что-то такое:
const int MyCoolControlType = 1111;
//...
case (ControlType)MyCoolControlType:
//...

А то последнее время гайки с enum'ами в switch стали подзакручивать. Хотя, можно и без switch обойтись, варианты есть.

2) Собственно, сам IControlPainter.

2.а Можно сделать
void IControlPainter::paintControl(ICanvas *pCanvas, IControl *pControl)

И пусть там из pControl динамик кастят в нужный тип контрола, извлекают из него всю необходимую для отрисовки инфу, и рисуют. Но опять же, это, как минимум, не очень бинарно совместимо. Но, тут можно в одном типе пайнтера сделать отрисовку всех контролов

2.б Можно сделать каст в хосте
IControlPainter* pBasePainter = pControlPainterFactory->getPainter(...)
IPushButtonPainter* pPainter = dynamic_cast<IPushButtonPainter>(pBasePainter);
pPainter->paintPushButton(pCanvas, this);


Тут большой минус в том, что хост должен знать обо всех типах контролов и пайнтеров для них

2.в
Можно сделать с одним интерфейсом IControlPainter, а тип и состояние передавать enum/int'ами
enum ControlStatesPushButton{ Pushed, Unpushed };
enum ControlStatesCheckBox{ Checked, Unchecked };
enum ControlStatesCheckBox3State{ Checked, Unchecked, Third };

void IControlPainter::paintControl(ICanvas *pCanvas, IControl *pControl, ControlType controlType, int controlState)


Тут уже суровый кастинг в int. Хотя, в принципе: I) все эти состояния кнопок можно объединить в один enum с тремя состояниями, в ButtonBase сделать флаг — она 2State или 3State, но это просто недостаток моего примера; II) Отдельно можно не передавать int controlState, а просто у базового контрола будет метод int getControlState() — но это те же яйца, только в профиль

Но данный вариант позволяет, как и (а) сделать отрисовку разных типов контролов в одном типе пайнтера.

Какие ещё недостатки есть в описанных мной сценариях?

Какие ещё есть варианты реализации подобного, с учетом того, что: а) хотелось бы получить бинарную совместимость между разными компиляторами; б) я буду это запихивать в скрипт-движок, и он близок скорее к сишечке; в) я хочу дать пользователю возможность самому делать ссвои контролы как в отдельных DLL, так и в скрипте
Маньяк Робокряк колесит по городу
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.