一种更清晰的代码替代多态

时间:2011-01-16 22:20:00

标签: c++ inheritance polymorphism

构建GUI系统,我有一些类用于从基础“GUIcontrol”类派生的不同GUI组件。我想要的是只有一个函数来返回任何类型的组件,但能够使用特定于该组件类型的函数(派生类的函数)。我注意到多态方法将成为一个问题,我必须声明基础中的所有派生函数,这是不必要的,因为我永远不会只从基类创建一个对象。

class GUIcontrol {
  protected:
    std::string _name;
    // these two methods (along with name()) will be used by all types
    virtual void position(/*parameters*/)
    virtual void useImage(/*parameters*/)

    // these should be only in derived types
    virtual void setHotSpot(/*parameters*/);
    virtual void setScrollButtons(/*parameters*/);
  public:
    std::string name();
  /*etc*/
}

class GUIbutton : public GUIcontrol {
  public:
    void setHotSpot(/*parameters*/);
}

class GUIscrollBar : public GUIcontrol {
  public:
    void setScrollButtons(/*parameters*/);
}

GUIcontrol* GUIsystem::getControl(std::string name);

这个问题是,如果我想添加GUIbutton或GUIscrollBar独有的更多函数,或任何其他派生GUI类的函数,我还必须在基类中声明它们是虚拟的,所以编译器不会抱怨像“setHotSpot”这样的东西不是它返回的基类的成员。

基类确实具有将应用于所有派生类的成员函数,例如告诉对象应该放在哪里,需要使用什么图像,应该调用什么图像等等。但是我不这样做想要保持基类与其他需要保持对某些派生类独占的函数的填充。

随着我不断添加更多虚函数,我最终会得到一个基类的巨大blob对象。我可以用更干净的方式设计吗?请注意,我仍然不确定是否要使用static_cast / dynamic_cast来获取getControl()以解决此问题,但只是想知道是否还有其他方法来清理它。

4 个答案:

答案 0 :(得分:4)

基类应包含所有控件共有的功能的方法。

如果您打算使用仅对一种控件有意义的功能,那么您应该检查控件的类型是否正确,然后可以将其转换为该类型。

答案 1 :(得分:1)

基类是独占常用功能。如果您希望方法对不同控件的行为不同,请使用dynamic_cast。如果您希望它对所有控件执行相同操作,请使用虚拟方法。

这是你的问题:

  

我想要的只是一个   函数返回任何类型的   组件,但能够使用   特定于该组件的功能   type(派生类的函数)。

你想要的是对待它们,但不同。呵呵。我想知道你将如何做这项工作。您需要决定是否要对它们进行相同的处理,或者您是否希望以不同方式对待它们。

答案 2 :(得分:0)

类型检查然后向下转换不是正确的方法。你应该做的是将泛型方法放在你的基类上,它执行你想要的操作的类型,然后在子类中重写它们。例如,如果您希望GUIControl能够绘制自己,那么在基类上放置一个doDraw()方法,然后根据需要覆盖每个子类中的方法。如果您在子类上放置getTitleBar(),getText()等方法,然后让调用者向下转换并根据类型调用这些特定方法,则封装会被破坏。如果您有一些通用代码,多个子类需要进行绘制,那么您可以通过另一个父类或通过组合将其分解出来。使用dynamic_cast或在通用子类上放置特定方法可能会使您的代码变得更糟。

答案 3 :(得分:0)

如果我有这个权利:您希望能够传递基类对象,但是有一种干净的方法来调用派生类实现这些方法的特定派生类方法吗?

听起来像'mixin'模式可能会有所帮助:

struct Base
{
    virtual ~Base() {}
};


struct Mixin
{
    virtual ~Mixin() {}
    virtual void mixedMethod() = 0;
};

struct Concrete : Base, Mixin
{
    virtual void mixedMethod() { std::cout << "Mixing" << std:: endl; }
};

Base* create() { return new Concrete;}

bool mixIt(Base& b)
{
    Mixin* m = dynamic_cast<Mixin*>(&b);
    if (m)
        m->mixedMethod();
    return m;
}    

void test ()
{
    Base* b = create();
    assert(mixIt(*b));
    Base base;
    assert(!mixIt(base));   
}

[是的,真正的代码从不使用struct来进行多态类;只是保持紧凑。]

这里的想法是给定方法的可用性封装在Mixin类中,这是一个纯抽象基类,可能只有一个纯虚函数。 如果你想“知道”你的基类对象属于派生类型,你可以调用mixin类方法。您可以将测试和调用包装在非成员函数中;这允许您保持基本calss接口本身清洁。