类需要处理一个新类型 - 重复还是扩展?

时间:2010-07-20 20:58:54

标签: c++ oop

假设我有一个宠物商店的C ++应用程序,它开始于Cats。该应用程序有一个类,它为Cat对话框执行一些数据处理:

CatDlg.cpp:

CatDlg::CatDlg() {//ctor stuff}

CatDlg::onInitDialog() {
   DBAccess db;
   db.open();
   CatRec cat;
   cat = db.findRec(m_catID);
   CString name = cat.getName();

   NameField.SetWindowText(name);
   // other catty dialogy stuff
}

现在商店所有者想要将Dogs添加到应用程序中。除数据类型外,Dog数据与Cat数据基本相同。我可以复制CatDlg类,更改几种类型并以这种方式处理它,但这导致维护问题和代码膨胀,99%重复代码:

DogDlg.cpp:

DogDlg::onInitDialog() {
   DBAccess db;
   db.open();
   DogRec dog;
   dog = db.findRec(m_dogID);
   CString name = dog.getName();

   NameField.SetWindowText(name);
   // other doggy dialogy stuff
}

或者,我可以通过传入指示动物类型的标志来更改对话框类以处理多个动物,然后相应地处理它。当主人添加新动物时,这也会变得更加丑陋,因为该方法变得更长,有许多重复的代码块:

AnimalDlg.cpp:

AnimalDlg::onInitDialog(AnimalType type) {
    DBAccess db;
    db.open();
    if(type == CAT)
    {
        CatRec cat;
        cat = db.findRec(m_catID);
        CString name = cat.getName();
    }
    else if(type == DOG)
    {
        DogRec dog;
        dog = db.findRec(m_dogID);
        CString name = dog.getName();
    }

   NameField.SetWindowText(name);
}

重构课程以使其更通用的最佳方法是什么?是否有可以应用的设计模式?有没有办法将公共代码拉入基类,然后制作特定于动物的子类?我快速浏览了福勒关于重构的书,但没有任何东西在我身上跳过。

假设该类中有更多代码,目前特定于Cat类型,但适用于任何类型的动物。

感谢您的帮助!

菲利普

4 个答案:

答案 0 :(得分:5)

这为继承而尖叫。定义动物的基类。然后派出猫狗和条纹海狸以及商店想要出售的任何其他东西。

答案 1 :(得分:2)

假设CatRec和DogRec是不相关的类型,不能成为继承结构的一部分,我认为你需要使用模板。

AnimalDlg将是CatDlgDogDlg的父级。 AnimalDlg将有一个新的受保护模板findRec函数,其返回类型将是模板类型(因此您可以返回CatRec或DogRec)。 onInitDialog仅存在于AnimalDlg中并调用虚函数getName而不是db特定代码。然后,getName的子版本将使用findRec<CatDlg>的相应版本(例如findRec)来获取记录并将类型返回到onInitDialog

如果我有机会,我会稍后尝试草拟代码。

编辑(代码草图):

class AnimalDlg
{
public:
    void onInitDialog()
    {
        NameField.SetWindowText(getName());
    }

protected:
    template <class T>
    T findRec(IdType id) const
    {
        DBAccess db;
        db.open();
        return T(db.findRec(id));
    }

    virtual std::string getName() const = 0;
};

class CatDlg : public AnimalDlg
{
protected:
    virtual std::string getName() const
    {
        return findRec<CatRec>(m_catID).getName();
    }
};

答案 2 :(得分:1)

将您的数据库和数据直接绑定到GUI并不是那么好。如果你想要的只是CRUD(创建读取更新删除),那么有很多工具可以用数据库来完成。

围绕应用程序的目的驱动GUI,而不是如何存储数据。

答案 3 :(得分:0)

我会沿着模板专业化的路线前进:

template <class AnimalType>
AnimalDlg::onInitDialog(AnimalType type, int m_animalId) {
   DBAccess db;
   db.open();

   name = db.findRec<AnimalType>(m_animalId).getName();

   NameField.SetWindowText(name);
}