背景:
ObjectListModel
,其继承QAbstractListModel
并包含QObjectList
。对象是行,它们的属性是列(使用QMetaObject
设置),并且通知更改将传播到视图。还有一些容器助手(开始/结束/迭代器/大小),以便我可以迭代存储的QObject。 TypedObjectListModel<T>
,它提供了类型安全性(主要是通过覆盖push_back
et.al.并定义iterator
到{T}}的新static_cast
类型)。 当我只有一种类型的物体时,这一切都很有效。我只是创建了一个新类(f.ex。FruitsModel
,其中包含Q_OBJECT
,并继承TypedObjectListModel<Fruit>
。这只能包含Fruits或Fruit-subobjects。
但是,我现在有一个可以在两种不同状态下运行的应用程序。在第二种状态下,模型应该只包含苹果,没有香蕉(或者水果,这是一个具体的基类)。
所以,我想创建一个ApplesModel
类型,它应该继承FruitsModel
并只改变T的类型。这让我陷入困境,因为我得到了继承钻石死亡:
QObject
|
QAbstractListModel
|
ObjectListModel -------------------
| |
TypedObjectListModel<Fruit> TypedObjectListModel<Apple>
| |
FruitsModel -------------------ApplesModel
这在概念上也是错误的,因为FruitsModel :: push_back(Fruit *)在ApplesModel中是非法的。但是,读取/迭代水果(不仅仅是苹果)应该是可能的。
另外,我在FruitsModel(findFruitById
)中有一些应该被覆盖的函数,只返回ApplesModel中的Apples。
在C ++中解决此问题的首选设计模式是什么?
我怀疑(希望)我不是第一个尝试做类似事情的人。
我尝试过很多想法,但我陷入了各种各样的死胡同。您认为ObjectListModel的虚拟继承可以解决问题,但后来我使用QObject::findChild
得到了这个:
error C2635: cannot convert a 'QObject*' to a 'ApplesModel*'; conversion from a virtual base class is implied
以上可以通过我自己的findChild实现来解决,而不是使用dynamic_cast,但仍有一些死胡同。
template<typename T>
inline T myFindChild(const QObject *parent, const QString &name = QString())
{
return dynamic_cast<T>(qt_qFindChild_helper(parent, name, reinterpret_cast<T>(0)->staticMetaObject));
}
更新
geekp有以下建议:
从Fruit继承Apple,不要打扰ApplesModel
我如何强制执行只有applesModel中的苹果?还有,我 每次我拿苹果(作为水果)都需要垂头丧气。
不要从FruitsModel继承(如果你不使用它,你为什么会这样做? 方法?)
我正在使用一些方法,尤其是阅读方法。
不要继承Apple的TypesObjectListModel,只继承FruitsModel。
与AppleModel无关的缺点。
答案 0 :(得分:2)
因此,读取和写入操作在继承方面存在根本性的不同。
回到OOP 101,还记得关于方形和矩形的比喻吗?人们常说广场是一种矩形,但只有在阅读时才是这样。
写作时,正方形不是矩形,但矩形是各种正方形!
即:
bool test( Rectangle* r ) {
int old_height = r->GetHeight();
int old_width = r->GetWidth();
r->SetWidth(old_width+100);
return old_height == r->GetHeight();
}
上面的函数为所有“真实”矩形返回true
,但对于Squares
,它可能不会。因此SetWidth
违反了Rectangle
合理Square
的合同。
另一方面,Rectangle
的每个Square
接口都由struct IRectangleRead { ... };
struct ISquareRead { ... };
struct ISquareWrite: virtual ISquareRead { ... };
struct IRectangleWrite:ISquareWrite, virtual IRectangleRead { ... };
struct ConstRectangle: virtual IRectangleRead { ... };
struct ConstSquare: virtual ISquareRead, virtual IRectangleRead { ... };
struct Rectangle: ConstRectangle, IRectangleWrite { ... };
struct Square: ConstSquare, ISquareWrite { ... };
完美处理。
这可能会让你像这样一团糟:
{{1}}
生成一堆继承层次结构,但是可以在每个方法上放置限制性契约,并且实现该方法的每个对象都将遵循它们。
现在,您应该注意,如果您的对象是不可变的,上面的内容会变得非常简单。然后唯一的写作形式是通过工厂功能,事情变得整洁。
所以这里的具体课程 - 拆分阅读和修改代码的各个部分。公共修改部分(适用于基类)未公开,因为在子类情况下操作无效。
公共阅读部分是公开暴露的,子类型阅读部分也是如此。
子类型编写代码转发到私有公共基类编写代码。
答案 1 :(得分:0)
多种选择: