Вопросы, связанные с внедрением шаблона проектирования посетителей

Исходя из моих текущих требований, я реализовал шаблон Visitor, и у нас есть несколько различных аспектов:

1> В отличие от классического примера, проиллюстрированного в книге GoF, подклассы Equipment не выровнены. Другими словами, все подклассы находятся в иерархии.

Например: // Плоское оборудование A: общественное оборудование {} Оборудование B: общественное оборудование {} Оборудование C: общественное оборудование {}

//hierarchy
EquipmentA : public Equipment {}
EquipmentB : public EquipmentA {}
EquipmentC : public EquipmentB {}

2> Подкласс имеет некоторые специализированные функции, которые не являются виртуальными функциями, определенными в базе. Например:

`EquipmentA` may define a function called `GetFactorRate`
`EquipmentB` may define a function called `GetAmplifyRate`

Имейте в виду все это, вот мой код.

Вопрос 1> Есть ли в коде проблемы?

Вопрос 2> Должен ли я относиться к блоку, который я пометил как "Примечание", то есть что если SpecialFloppyDisk звонки VisitFloppyDisk вместо SpecialVisitFloppyDisk, Я считаю, что хороший код разработан таким образом, чтобы было трудно совершать ошибки. Но я не уверен, применим ли этот случай здесь.

//////////////////////////////////////////////////////////////////////////
class FloppyDisk;
class SpecialFloppyDisk;
class EquipmentVisitor
{
public:
    virtual ~EquipmentVisitor() {}    
    // modify Equipment based on different subclass of Equipment
    virtual void VisitFloppyDisk(FloppyDisk&) = 0;
    virtual void VisitSpecialFloppyDisk(SpecialFloppyDisk&) = 0;

    int GetTotalPrice() const { return m_iTotalPrice; }
protected:
    int m_iTotalPrice;    
protected:
    EquipmentVisitor() {}
};    
//////////////////////////////////////////////////////////////////////////
class Equipment {
public:
    virtual ~Equipment() {}    
    // return the price of the Equipment
    virtual int GetPrice() const = 0;

    virtual void Accept(EquipmentVisitor&) = 0;
protected:
    Equipment() {}
};
//////////////////////////////////////////////////////////////////////////
class FloppyDisk : public Equipment
{
public:
    // return the price of the Equipment
    virtual int GetPrice() const {return 100;}
    int GetFactorRate() const {return 2; } // x 2

    virtual void Accept(EquipmentVisitor& e){e.VisitFloppyDisk(*this);}
};
//////////////////////////////////////////////////////////////////////////
class SpecialFloppyDisk : public FloppyDisk
{
public:
    virtual std::string GetName() const {return std::string("Bus");}            
    // return the price of the Equipment
    virtual int GetPrice() const {return 20000;}

    int GetAmplifyRate() const {return 11; }// x 11
    virtual void Accept(EquipmentVisitor& e)
    {
        e.VisitSpecialFloppyDisk(*this);
        // Note: if called the following function by accident, then it introduces
        // hidden bugs!!!!
        // e.VisitFloppyDisk(*this);
    }
};
//////////////////////////////////////////////////////////////////////////
class PricingVisitor : public EquipmentVisitor
{
public:
    virtual void VisitFloppyDisk(FloppyDisk& e)
    {m_iTotalPrice = e.GetPrice() * e.GetFactorRate();}
    virtual void VisitSpecialFloppyDisk(SpecialFloppyDisk& e)
    {   m_iTotalPrice = e.GetPrice() * e.GetAmplifyRate(); }
};
//////////////////////////////////////////////////////////////////////////
int _tmain(int argc, _TCHAR* argv[])
{
    PricingVisitor pricingVisitor;
    SpecialFloppyDisk specialFloppyDisk;
    FloppyDisk floppyDisk;

    floppyDisk.Accept(pricingVisitor);
    // output: pricingVisitor.GetTotalPrice(): 200
    // i.e. 100 x 2
    std::cout << "pricingVisitor.GetTotalPrice(): " << pricingVisitor.GetTotalPrice() << std::endl;

    // output: pricingVisitor.GetTotalPrice(): 220000
    // i.e. 20000 x 11
    specialFloppyDisk.Accept(pricingVisitor);
    std::cout << "pricingVisitor.GetTotalPrice(): " << pricingVisitor.GetTotalPrice() << std::endl;

    return 0;
}

1 ответ

Q1: есть проблемы? Да. Проблемы дизайна:

  1. EquipmentVisitor не должен иметь GetTotalPrice()/m_iTotalPrice, Это должен быть чистый класс, содержащий только материал Visit. Шаблон посетителя в основном предназначен для добавления новых функций в семейство классов (в данном случае "Оборудование") без изменения этих классов. Если добавить другого посетителя - тогда его, скорее всего, не интересуют цены на оборудование. Если он будет заинтересован в ценообразовании, то он должен быть выведен из PricingVisitor,
  2. Переместить ценовые вещи в PricingVisitor, это правильное место для этого.
  3. Не делайте разные имена для разных Visit методы. Компилятор выберет правильный метод в Accept основываясь на *this тип.

Q2: что если SpecialFloppyDisk вызывает VisitFloppyDisk вместо SpecialVisitFloppyDisk:

Уже упоминалось в Q1.3 - не используйте разные имена - таким образом вы избежите проблем.

class EquipmentVisitor
{
public:
   virtual ~EquipmentVisitor() {}    
   virtual void Visit(FloppyDisk&) = 0;
   virtual void Visit(SpecialFloppyDisk&) = 0;
};  
Другие вопросы по тегам