Visitor (Инспектор, Посетитель)

Помечено: , , ,

В этой теме 0 ответов, 1 участник, последнее обновление  Васильев Владимир Сергеевич 7 мес., 2 нед. назад.

  • Автор
    Сообщения
  • #3623

    Предположим, у вас есть иерархия классов. Пусть это будут деревья. Базовый класс «Дерево» и его наследники «Ель», «Береза», «Сосна», и так далее. Кроме того, у вас есть контейнер указателей на «Дерево». Ваша задача — научиться осуществлять конкретные действия над конкретным типом деревьев. Предположим, что надвигается Новый год, и вам надо спились елку, после чего нарядить ее елочными игрушками. В этом вам поможет паттерн Visitor.

    #include <iostream>
    #include <memory>
    #include <vector>
    #include <boost/shared_ptr.hpp>
    
    template <class T>
    class TVisitor
    {
    public:
    
        virtual ~TVisitor() {}
    
        virtual void Accept(T& t) = 0;
    };
    
    class Fir;
    class Pine;
    
    class Tree
    {
    public:
    
        virtual ~Tree() {}
    
        virtual void Visit(TVisitor<Fir>&  v) {}
        virtual void Visit(TVisitor<Pine>& v) {}
    };
    
    typedef boost::shared_ptr<Tree> TreePtr;
    
    class Fir : public Tree
    {
    public:
    
        virtual void Visit(TVisitor<Fir>& v)
        {
            v.Accept(*this);
        }
    };
    
    class Pine : public Tree
    {
    public:
    
        virtual void Visit(TVisitor<Pine>& v)
        {
            v.Accept(*this);
        }
    };
    
    class SawFirVisitor : public TVisitor<Fir>
    {
    public:
    
        virtual void Accept(Fir& fir)
        {
            std::cout << "Fir sawed!" << std::endl;
        }
    };
    
    class DecorateFirVisitor : public TVisitor<Fir>
    {
    public:
    
        virtual void Accept(Fir& fir)
        {
            std::cout << "Fir decorated!" << std::endl;
        }
    };
    
    int main()
    {
        typedef std::vector<TreePtr> Forest;
        Forest forest;
    
        forest.push_back(TreePtr(new Fir));
        forest.push_back(TreePtr(new Pine));
        forest.push_back(TreePtr(new Pine));
        forest.push_back(TreePtr(new Fir));
        forest.push_back(TreePtr(new Fir));
    
        SawFirVisitor sawFir;
        DecorateFirVisitor decorateFir;
    
        for(Forest::iterator i = forest.begin(); i != forest.end(); ++i)
        {
            (*i)->Visit(sawFir);
            (*i)->Visit(decorateFir);
        }
    }

    Если вы впервые слышите об этом паттерне, то возможно, что сразу же понять логику его работы будет несколько сложно. Однако, ничего сложного в действительности здесь нет. Просто прогоните программу под дебаггером в пошаговом режиме, и вам все станет понятно.

    Визиторы, в некотором смысле, это возможность добавлять в иерархию виртуальные функции, не затрагивая саму иерархию.

    Данный паттерн имеет довольно забавную основную идею, но при этом совершенно ужасные практические свойства. Скажу сразу, что Visitor — плохой паттерн.

    Во-первых, базовому классу «Дерево» придется знать обо всех типах визиторов, которые он умеет принимать. В моем примере визитор — шаблонный класс. Однако и это не спасает от зависимостей. Базовому инспектируемому классу необходимо знать о своих дочерних классах на уровне неполного объявления.

    Во-вторых, чтобы чтобы можно было работать с новой реализацией инспектируемой иерархии, для нее потребуется написать новый визитор. После чего добавить в базовый класс метод, принимающий новоиспеченный визитор. То есть при добавлении нового класса, придется переписывать существующие классы.

    И, наконец, в третьих, внося какие-либо изменения в инспектируемую иерархию, Вы всегда будете рисковать работоспособностью уже существующих визиторов. Визиторы связывают существующую иерархию по рукам и ногам, и попытка внести в нее какие-либо изменения может обернуться для вас рефакторингом визиторов в пугающих масштабах.

    Надеюсь, что данная статья носила для вас чисто ознакомительный характер, и что вы десять раз подумаете, прежде чем решите воспользоваться этим паттерном.

Для ответа в этой теме необходимо авторизоваться.