수색…


C ++의 방문객 패턴 예제

대신에

struct IShape
{
    virtual ~IShape() = default;

    virtual void print() const = 0;
    virtual double area() const = 0;
    virtual double perimeter() const = 0;
    // .. and so on
};

방문객을 사용할 수 있습니다 :

// The concrete shapes
struct Square;
struct Circle;

// The visitor interface
struct IShapeVisitor
{
    virtual ~IShapeVisitor() = default;
    virtual void visit(const Square&) = 0;
    virtual void visit(const Circle&) = 0;
};

// The shape interface
struct IShape
{
    virtual ~IShape() = default;

    virtual void accept(IShapeVisitor&) const = 0;
};

이제 구체적인 모양 :

struct Point {
    double x;
    double y;
};

struct Circle : IShape
{
    Circle(const Point& center, double radius) : center(center), radius(radius) {}
    
    // Each shape has to implement this method the same way
    void accept(IShapeVisitor& visitor) const override { visitor.visit(*this); }

    Point center;
    double radius;
};

struct Square : IShape
{
    Square(const Point& topLeft, double sideLength) :
         topLeft(topLeft), sideLength(sideLength)
    {}

    // Each shape has to implement this method the same way
    void accept(IShapeVisitor& visitor) const override { visitor.visit(*this); }

    Point topLeft;
    double sideLength;
};

방문객 :

struct ShapePrinter : IShapeVisitor
{
    void visit(const Square&) override { std::cout << "Square"; }
    void visit(const Circle&) override { std::cout << "Circle"; }
};

struct ShapeAreaComputer : IShapeVisitor
{
    void visit(const Square& square) override
    {
        area = square.sideLength * square.sideLength;
    }

    void visit(const Circle& circle) override
    {
         area = M_PI * circle.radius * circle.radius;
    }

    double area = 0;
};

struct ShapePerimeterComputer : IShapeVisitor
{
    void visit(const Square& square) override { perimeter = 4. * square.sideLength; }
    void visit(const Circle& circle) override { perimeter = 2. * M_PI * circle.radius; }

    double perimeter = 0.;
};

그리고 그것을 사용하십시오 :

const Square square = {{-1., -1.}, 2.};
const Circle circle{{0., 0.}, 1.};
const IShape* shapes[2] = {&square, &circle};

ShapePrinter shapePrinter;
ShapeAreaComputer shapeAreaComputer;
ShapePerimeterComputer shapePerimeterComputer;

for (const auto* shape : shapes) {
    shape->accept(shapePrinter);
    std::cout << " has an area of ";

    // result will be stored in shapeAreaComputer.area
    shape->accept(shapeAreaComputer);

    // result will be stored in shapePerimeterComputer.perimeter
    shape->accept(shapePerimeterComputer); 

    std::cout << shapeAreaComputer.area
              << ", and a perimeter of "
              << shapePerimeterComputer.perimeter
              << std::endl;
}

예상 출력 :

Square has an area of 4, and a perimeter of 8
Circle has an area of 3.14159, and a perimeter of 6.28319

데모

설명 :

  • void Square::accept(IShapeVisitor& visitor) const override { visitor.visit(*this); } 의 정지형 this 알려져 있으며, 따라서 선택 (컴파일시) 과부하는 void IVisitor::visit(const Square&); .

  • square.accept(visitor); 호출을 사용하면 virtual 통한 동적 디스패치는 호출 할 accept 을 파악하는 데 사용됩니다.

장점 :

  • 새 방문자를 추가하기 만하면 IShape 클래스에 새로운 기능 ( SerializeAsXml , ...)을 추가 할 수 있습니다.

단점 :

  • 새로운 콘크리트 모양 ( Triangle , ...)을 추가하려면 모든 방문자를 수정해야합니다.

모든 기능을 넣어의 다른 virtual 의 방법 IShape 반대 장단점이 있습니다 : 새로운 기능을 추가하면 기존의 모든 모양을 수정할 필요로하지만, 새로운 형태를 추가하면 기존 클래스에 영향을주지 않습니다.

자바의 방문자 패턴 예제

Visitor 패턴을 사용하면 클래스의 구조를 수정하지 않고 새 작업 또는 메서드를 클래스 집합에 추가 할 수 있습니다.

이 패턴은 객체를 확장하지 않고 객체에 특정 작업을 집중 시키거나 객체를 수정하지 않고 작업 할 때 특히 유용합니다.

위키 백과의 UML 다이어그램 :

여기에 이미지 설명을 입력하십시오.

코드 스 니펫 :

import java.util.HashMap;

interface Visitable{
    void accept(Visitor visitor);
}

interface Visitor{
    void logGameStatistics(Chess chess);
    void logGameStatistics(Checkers checkers);
    void logGameStatistics(Ludo ludo);    
}
class GameVisitor implements Visitor{
    public void logGameStatistics(Chess chess){
        System.out.println("Logging Chess statistics: Game Completion duration, number of moves etc..");    
    }
    public void logGameStatistics(Checkers checkers){
        System.out.println("Logging Checkers statistics: Game Completion duration, remaining coins of loser");    
    }
    public void logGameStatistics(Ludo ludo){
        System.out.println("Logging Ludo statistics: Game Completion duration, remaining coins of loser");    
    }
}

abstract class Game{
    // Add game related attributes and methods here
    public Game(){
    
    }
    public void getNextMove(){};
    public void makeNextMove(){}
    public abstract String getName();
}
class Chess extends Game implements Visitable{
    public String getName(){
        return Chess.class.getName();
    }
    public void accept(Visitor visitor){
        visitor.logGameStatistics(this);
    }
}
class Checkers extends Game implements Visitable{
    public String getName(){
        return Checkers.class.getName();
    }
    public void accept(Visitor visitor){
        visitor.logGameStatistics(this);
    }
}
class Ludo extends Game implements Visitable{
    public String getName(){
        return Ludo.class.getName();
    }
    public void accept(Visitor visitor){
        visitor.logGameStatistics(this);
    }
}

public class VisitorPattern{
    public static void main(String args[]){
        Visitor visitor = new GameVisitor();
        Visitable games[] = { new Chess(),new Checkers(), new Ludo()};
        for (Visitable v : games){
            v.accept(visitor);
        }
    }
}

설명:

  1. Visitable ( Element )은 인터페이스 Visitable 인터페이스 메소드는 일련의 클래스에 추가되어야합니다.
  2. Visitor 에 작업을 수행하는 방법을 포함하는 인터페이스이다 Visitable 소자.
  3. GameVisitorVisitor 인터페이스 ( ConcreteVisitor )를 구현하는 클래스입니다.
  4. Visitable 요소는 동의 Visitor 와의 관련 방법 호출 Visitor 인터페이스를.
  5. GameElement 로 취급하고 Chess,Checkers and Ludo 같은 구체적인 게임을 ConcreteElements 로 취급 할 수 있습니다.

위의 예에서 Chess, Checkers and Ludo 는 세 가지 게임 (및 Visitable 클래스)입니다. 좋은 하루에 나는 각 게임의 통계를 기록하는 시나리오를 만났습니다. 따라서 통계 기능을 구현하기 위해 개별 클래스를 수정하지 않고도 각 책임의 구조를 수정하지 않고도 GameVisitor 클래스에서 책임을 중앙 집중화 할 수 있습니다.

산출:

Logging Chess statistics: Game Completion duration, number of moves etc..
Logging Checkers statistics: Game Completion duration, remaining coins of loser
Logging Ludo statistics: Game Completion duration, remaining coins of loser

유스 케이스 / 적용 분야 :

  1. 구조에서 그룹화 된 여러 유형의 객체에 대해 유사한 작업을 수행해야합니다.
  2. 서로 다른 많은 관련없는 작업을 실행해야합니다. 객체를 Operation과 Object 구조로 구분합니다.
  3. 객체 구조를 변경하지 않고 새로운 작업을 추가해야합니다.
  4. 클래스 를 변경하거나 파생시키지 않고 관련 작업을 단일 클래스로 수집하십시오.
  5. 소스가 없거나 소스를 변경할 수없는 클래스 라이브러리에 함수를 추가하십시오.

추가 참조 :

oodesign

소스 제작

C ++의 방문자 예제

// A simple class hierarchy that uses the visitor to add functionality.
//
class VehicleVisitor;
class Vehicle
{
    public:
        // To implement the visitor pattern
        // The class simply needs to implement the accept method 
        // That takes a reference to a visitor object that provides
        // new functionality.
        virtual void accept(VehicleVisitor& visitor) = 0
};
class Plane: public Vehicle
{
    public:
        // Each concrete representation simply calls the visit()
        // method on the visitor object passing itself as the parameter.
        virtual void accept(VehicleVisitor& visitor) {visitor.visit(*this);}

        void fly(std::string const& destination);
};
class Train: public Vehicle
{
    public:
        virtual void accept(VehicleVisitor& visitor) {visitor.visit(*this);}

        void locomote(std::string const& destination);
};
class Automobile: public Vehicle
{
    public:
        virtual void accept(VehicleVisitor& visitor) {visitor.visit(*this);}

        void drive(std::string const& destination);
};

class VehicleVisitor
{
    public:
        // The visitor interface implements one method for each class in the
        // hierarchy. When implementing new functionality you just create the
        // functionality required for each type in the appropriate method.

        virtual void visit(Plane& object)      = 0;
        virtual void visit(Train& object)      = 0;
        virtual void visit(Automobile& object) = 0;

    // Note: because each class in the hierarchy needs a virtual method
    // in visitor base class this makes extending the hierarchy ones defined
    // hard.
};

예제 사용법 :

// Add the functionality `Move` to an object via a visitor.
class MoveVehicleVisitor
{
    std::string const& destination;
    public:
        MoveVehicleVisitor(std::string const& destination)
            : destination(destination)
    {}
    virtual void visit(Plane& object)      {object.fly(destination);}
    virtual void visit(Train& object)      {object.locomote(destination);}
    virtual void visit(Automobile& object) {object.drive(destination);}
};

int main()
{
    MoveVehicleVisitor  moveToDenver("Denver");
    Vehicle&            object = getObjectToMove();
    object.accept(moveToDenver);
}

대형 물체 이송

방문자 패턴은 구조를 탐색하는 데 사용할 수 있습니다.

class GraphVisitor;
class Graph
{
    public:
        class Node
        {
            using Link = std::set<Node>::iterator;
            std::set<Link>   linkTo;
            public:
                void accept(GraphVisitor& visitor);
        };

        void accept(GraphVisitor& visitor);

    private:
        std::set<Node>  nodes;
};

class GraphVisitor
{
    std::set<Graph::Node*>  visited;
    public:
        void visit(Graph& graph)
        {
            visited.clear();
            doVisit(graph);
        } 
        bool visit(Graph::Node& node)
        {
            if (visited.find(&node) != visited.end()) {
                return false;
            }
            visited.insert(&node);
            doVisit(node);
            return true;
        }
    private: 
        virtual void doVisit(Graph& graph)      = 0;
        virtual void doVisit(Graph::Node& node) = 0;
};

void accept(GraphVisitor& visitor)
{
    // Pass the graph to the visitor.
    visitor.visit(*this);

    // Then do a depth first search of the graph.
    // In this situation it is the visitors responsibility
    // to keep track of visited nodes.
    for(auto& node: nodes) {
        node.accept(visitor);
    }
}
void Graph::Node::accept(GraphVisitor& visitor)
{
    // Tell the visitor it is working on a node and see if it was 
    // previously visited.
    if (visitor.visit(*this)) {

        // The pass the visitor to all the linked nodes.
        for(auto& link: linkTo) {
            link->accept(visitor);
        }
    }
}


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow