У нас есть такой код. Сильно упрощаю, но всё-таки я про архитектуру, а не про компилируемость, верно?
enum class ObjType { GROUP, LEAF };
class Obj {
public:
virtual ObjType type() const = 0;
virtual size_t nChildren() const = 0;
virtual std::shared_ptr<Obj> child(size_t i) = 0;
virtual QString tableData(int column, int role) = 0;
};
class Group : public Obj { … };
class Leaf : public Obj { … };
Чем этот код плох? Всё дело в функции tableData().
1. Obj не должен знать, как выглядит таблица. И если, например, у группы вместо названия написано «17 листьев» — то он тоже не должен знать эти штуки.
2. Obj не должен подтягивать интерфейсные библиотеки — такие как интерфейсный фреймворк (Qt) и интернационализацию. Например, у той же утилиты мы когда-нибудь сделаем консольную версию для автоматической подгрузки данных.
Таким образом, назревает вторая таблица виртуальных методов. Основная, содержащая nChildren и child, пристраивается в код всегда. Дополнительная, содержащая tableData — только если имеем дело с графическим интерфейсом.
Можно, конечно, написать в QAbstractItemModel::data():
void MyModel::data(const QModelIndex& index, int role) {
Obj* obj = toObj(index);
switch (obj->type()) {
case ObjType::GROUP: {
// Куча всякой дряни с qobject_cast, switch(role), switch(index.column())
}
case ObjType::LEAF: {
// Такая же дрянь
}
}
}
Но это громоздко и некрасиво.
Вот как сделать «по-объектному»?