Класс должен обрабатывать новый тип - дублировать или расширять? - PullRequest
2 голосов
/ 21 июля 2010

Предположим, у меня есть приложение на 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 в приложение.Данные о собаках, по сути, совпадают с данными 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 ]

5 голосов
/ 21 июля 2010

Это крики о наследовании. Определите базовый класс для животных. Затем найдите кота и собаку, полосатого бобра и все, что магазин хочет продать.

2 голосов
/ 21 июля 2010

Предполагая, что CatRec и DogRec являются несвязанными типами, которые нельзя сделать частью структуры наследования, я думаю, вам нужно использовать шаблоны.

AnimalDlg будет родителем CatDlg и DogDlg. AnimalDlg будет иметь новый защищенный шаблон findRec функция, тип возвращаемого значения которого будет типом шаблона (чтобы вы могли возвращать CatRec или DogRec). onInitDialog будет существовать только в AnimalDlg и будет вызывать виртуальную функцию getName вместо кода, специфичного для БД. Дочерние версии 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();
    }
};
1 голос
/ 21 июля 2010

Не так хорошо привязывать вашу БД и данные напрямую к графическому интерфейсу. Если все, что вам нужно, это CRUD (Create Read Update Delete), то есть много инструментов, доступных для этого с БД.

Обращайте внимание на графический интерфейс пользователя, а не на то, как оно хранит данные.

0 голосов
/ 21 июля 2010

Я бы пошел по линии специализации шаблона:

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

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

   NameField.SetWindowText(name);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...