打开对象的实际类型的正确方法是什么?

时间:2012-07-02 16:39:57

标签: c++ oop qt design-patterns sax

我正在编写一个xml解析器,我需要一般地将对象添加到类中,然后切换对象的实际类型。问题是,我想保留一个简单的addElement(BaseClass *)接口然后正确放置对象。

void E_TableType::addElement(Element *e)
{
    QString label = e->getName();
    if (label == "state") {
        state = qobject_cast<E_TableEvent*>(e);
    }
    else if (label == "showPaytable") {
        showPaytable = qobject_cast<E_VisibleType*>(e);
    }
    else if (label == "sessionTip") {
        sessionTip = qobject_cast<E_SessionTip*>(e);
    }
    else if (label == "logoffmedia") {
        logoffMedia = qobject_cast<E_UrlType*>(e);
    }
    else {
        this->errorMessage(e);
    }
}

这是调用类,一个对象工厂。 myElement是E_TableType的一个实例。

F_TableTypeFactory::F_TableTypeFactory()
{
    this->myElement = myTable = 0;
}

void F_TableTypeFactory::start(QString qname)
{
     this->myElement = myTable = new E_TableType(qname);
}

void F_TableTypeFactory::fill(const QString& string)
{
  // don't fill complex types.
}

void F_TableTypeFactory::addChild(Element* child)
{
    myTable->addElement(child);
}

Element* F_TableTypeFactory::finish()
{
    return myElement;
}

void F_TableTypeFactory::addAttributes(const QXmlAttributes &attribs) {
    QString tName = attribs.value(QString("id"));
    myTable->setTableName(tName);
}

3 个答案:

答案 0 :(得分:2)

您是否考虑过使用多态?如果每个具体类都可以实现一个通用接口,那么所有这些代码都会消失,事情变得简单,将来也很容易改变。例如:

class Camera {
public:
    virtual void Init() = 0;
    virtual void TakeSnapshot() = 0;
}

class KodakCamera : Camera {
public:
    void Init() { /* initialize a Kodak camera */ };
    void TakeSnapshot() { std::cout << "Kodak snapshot"; }
}

class SonyCamera : Camera {
public:
    void Init() { /* initialize a Sony camera */ };
    void TakeSnapshot() { std::cout << "Sony snapshot"; }
}

所以,我们假设我们有一个包含硬件设备的系统,在这种情况下是一个摄像头。每个设备都需要不同的逻辑来拍照,但代码必须支持具有任何支持的摄像头的系统,因此我们不希望在我们的代码中散落交换语句。所以,我们创建了一个抽象类Camera

每个具体类(即SonyCameraKodakCamera)实现将包括不同的标题,链接到不同的库等,但它们都共享一个共同的接口;我们只需要决定在前面创建哪一个。所以......

std::unique_ptr<Camera> InitCamera(CameraType type) {
    std::unique_ptr<Camera> ret;
    Camera *cam;
    switch(type) {
    case Kodak:
        cam = new KodakCamera();
        break;
    case Sony:
        cam = new SonyCamera();
        break;
    default:
        // throw an error, whatever
        return;
    }

    ret.reset(cam);
    ret->Init();
    return ret;
}

int main(...) {
    // get system camera type
    std::unique_ptr<Camera> cam = InitCamera(cameraType);
    // now we can call cam->TakeSnapshot 
    // and know that the correct version will be called.
}

所以现在我们有一个实现Camera的具体实例。我们可以调用TakeSnapshot而无需在代码中的任何位置检查正确的类型,因为它无关紧要;我们知道将调用正确硬件的正确版本。希望这有帮助。

根据您的评论如下:

  

我一直在尝试使用多态,但我认为元素差别太大。例如,E_SessionTip有一个数量和状态元素,其中E_Url只有一个url。我可以在属性系统下统一这个但是然后我完全失去了所有好的打字。如果你知道这种方法可行,我愿意接受建议。

我建议将编写XML数据的责任传递给共享公共接口的类型。例如,而不是像这样的东西:

void WriteXml(Entity *entity) {
   switch(/* type of entity */) {
      // get data from entity depending
      // on its type and format
   }

   // write data to XML
}

做这样的事情:

class SomeEntity : EntityBase {
public:
    void WriteToXml(XmlStream &stream) {
        // write xml to the data stream.
        // the entity knows how to do this,
        // you don't have to worry about what data
        // there is to be written from the outside
    }
private:
    // your internal data
}

void WriteXml(Entity *entity) {
    XmlStream str = GetStream();
    entity->WriteToXml(stream);
}

这对你有用吗?我以前完成了这个,它对我有用。让我知道。

答案 1 :(得分:1)

查看访客模式,它可能对您有所帮助

答案 2 :(得分:1)

Double-dispatch可能会引起关注。表(在您的情况下)将调用基本元素的虚方法,该方法又调用回表。第二次调用是使用对象的动态类型进行的,因此在Table类中找到了适当的重载方法。

#include <iostream>

class Table;  //forward declare
class BaseElement
{
public:
   virtual void addTo(Table* t);
};
class DerivedElement1 : public BaseElement
{
   virtual void addTo(Table* t);
};
class DerivedElement2 : public BaseElement
{
   virtual void addTo(Table* t);
};
class Table
{
public:
   void addElement(BaseElement* e){ e->addTo(this); }
   void addSpecific(DerivedElement1* e){ std::cout<<"D1"; } 
   void addSpecific(DerivedElement2* e){ std::cout<<"D2"; } 
   void addSpecific(BaseElement* e){ std::cout<<"B"; } 
};
void BaseElement::addTo(Table* t){ t->addSpecific(this); }
void DerivedElement1::addTo(Table* t){ t->addSpecific(this); }
void DerivedElement2::addTo(Table* t){ t->addSpecific(this); }

int main()
{
Table t;
DerivedElement1 d1;
DerivedElement2 d2;
BaseElement b;

t.addElement(&d1);
t.addElement(&d2);
t.addElement(&b);
}

output: D1D2B