如何正确组织继承类以利用多态性

How do I properly organize my inheritance classes to take advantage of polymorphism?

本文关键字:多态性 继承 何正确      更新时间:2023-10-16

我正在尝试重新设计我的项目类。我很难想象事情应该如何运作。

当前实施:

class Item : public QGraphicsItem
{
public:
typedef enum  { PolygonType = QGraphicsItem::UserType + 1 } ShapeType;
Item() {...} 
Item(const Item &copyItem) // copy constructor
{   m_shapeType = copyItem.getItemShape();
m_color = copyItem.getItemColor();
setPos(copyItem.pos()); ...}
QRectF boundingRect() const;
QPainterPath shape() const;
void setItemShape(ShapeType s) { m_shapeType = s; }
ShapeType getItemShape() const { return m_shapeType; }
int type() const { return m_shapeType; }   // this will replace the above to allow QGraphicsItem casts
void setItemColor(QColor c)    { m_color = c; }
QColor getItemColor() const    { return m_color; }
....
protected:
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget);
QColor m_color;
ShapeType m_shapeType;
....
};
QPainterPath Item::shape() const
{
QPainterPath path;
switch (m_shapeType)
{
case Item::PolygonType:
...
break;
....
}
}
QRectF Item::boundingRect() const
{
return QRectF(...);
}
void Item::paint(QPainter *painter, const QStyleOptionGraphicsItem */*option*/, QWidget */*widget*/)
{
switch (m_shapeType)
{
case Item::PolygonType:
painter->drawPolygon(shape().toFillPolygon());
break;
....
}
}

根据选择,我创建一个项目并添加到画布中。。。然后,一旦进行了所有更改,我就将其添加到QGraphicsScene中。

所需任务:

场景处理包含类型的迭代

Item* item1 = new Item(*renderArea->item);
// make some changes on the item copy, then add it to a scene
collectionView->scene()->addItem(item1);
// in another function...
for(int i = 0; i < collectionView->scene()->items().size(); ++i)
{        
Item* item = new Item(*(dynamic_cast<Item*>(
collectionView->scene()->items().at(i))));
...
}

我正在努力实现的目标

这是一种最小的方法,但我必须将功能扩展到扩展其他类的项,如QGraphicsPixmapItem(不必为每个不需要它的项添加像素图)或QGraphicsSvgItem(同样,不必为不需要的项添加太多开销)。

所以我正在尝试:

class PolyItem : public Item
{
public:
PolyItem(): Item()
{
m_shapeType = Item::PolygonType;
}
PolyItem(PolyItem& copy): Item::Item(copy)
{
}
virtual void paint(QPainter* painter,
const QStyleOptionGraphicsItem* option,
QWidget* widget = NULL)
{
painter->drawPolygon(shape().toFillPolygon());
}
};

class PixMapItem : public Item, public QGraphicsPixmapItem
{
public:
PixMapItem(): Item()
{
m_shapeType = Item::PixmapType;
setShapeMode(QGraphicsPixmapItem::BoundingRectShape);
}
PixMapItem(PixMapItem& copy): Item::Item(copy)
{
setPixmap(copy.pixmap());
setShapeMode(QGraphicsPixmapItem::BoundingRectShape);
}
virtual void paint(QPainter* painter,
const QStyleOptionGraphicsItem* option,
QWidget* widget = NULL)
{
painter->drawPixmap(boundingRect(), pixmap());
}
};

问题:

  • 如何使其工作?当我选择一个形状时,我如何创建PolygonItem的实例(并且仍然能够使用通用项来修改其他属性,如颜色?)

----->我想在类Item中创建一个不同的构造函数来创建正确的形状,但这似乎意味着Item需要类多边形,而类多边形需要类Item。。。循环依赖?

  • 更复杂的是,我如何执行上面的任务,修改通用项,迭代项,而不管形状如何

我无法看到的东西:

collectionView->scene()->addItem(item1);

如果我的项目是多边形类型,会调用Item::paint()函数和所有其他相关函数,还是调用PolyItem::paint()
我是否必须插入切换用例代码来确定和强制转换每种类型的每一个实例?

也许值得将您的Item类缩减为BaseItem,其中包含您自己添加到Item的所有内容,以及您希望任何Item具有的所有功能(如果没有QGraphics*就无法实现,则视为纯虚拟):

class BaseItem {
public:
void setItemColor(QColor c)    { m_color = c; }
QColor getItemColor() const    { return m_color; }
....
protected:
QColor m_color;
ShapeType m_shapeType;
};

然后

class Item : public BaseItem, public QGraphicsItem
class PixMapItem : public BaseItem, public QGraphicsPixmapItem

等等——避免循环依赖。任何Item都可以称为BaseItem,并提供由BaseItem定义的通用接口。每个子级都向适当的QGraphics*提供该接口的专门化。

原始设计的问题是,该项从QGraphicsItem继承而来,而它不应该继承,因为您计划将其进一步专门化为QGraphicsPixmapItem,它本身已经包含QGraphicsItem。

编辑

现在,当你迭代所有项目时,你将它们视为BaseItem-这意味着BaseItem必须提供所有项目都遵循的接口

class BaseItem {
virtual void AddToScene(QGraphicsScene* scene) = 0;
// Caller takes ownership.
virtual BaseItem* CreateCopy() = 0;
};

所需任务:

场景处理包含类型的迭代

BaseItem* item1 = renderArea->item->CreateCopy();
// make some changes on the item copy, then add it to a scene
item1->AddToScene(collectionView->scene());

每个派生类实现由BaseItem定义的接口,同时在泛型操作中将所有项视为BaseItem。这也使得代码易于扩展——添加新的Item类型只需要实现派生类,但现有代码保持原样——因此需要仔细设计BaseItem,为所有项的通用操作提供接口。

class Item : public BaseItem, public QGraphicsItem {
void AddToScene(QGraphicsScene* scene) override { scene->addItem(this); }
// Caller takes ownership.
BaseItem* CreateCopy() override { return new Item; }
};

根据运行时决策创建不同的对象是抽象工厂模式的典型用例。在你的情况下,你可以有:

class ItemFactory
{
public:
virtual Item* create() = 0;    
};

然后:

class PixMapItemFactory : public ItemFactory
{
public:
PixMapItemFactory(/* possibly some configuration parameters */);
Item* create();  // will return a PixMapItem instance
};

然后,您可以对ItemFactory*类型进行操作,而不知道也不关心您正在创建的特定类型是什么。它将返回Item*实例,因此无论具体实例类型如何,都可以更改它们的公共属性。