본문 바로가기

디자인패턴

방문자(Visitor) 패턴

728x90

🎯 의도

  • 연산을 적용할 원소의 클래스를 변경하지 않고도 새로운 연산을 정의 할 수 있습니다.

📜 구현방법

  • 인터페이스 : 방문자(Visitor), 수락자(Acceptor)

방문자(Visitor) : 방문자는 다양한 수락자를 받아들이는 visit 인터페이스를 상속받고, 다형성을 갖도록 합니다. 그리고 각각의 다형성이 있는 함수안에서 기능들을 구현합니다.

요소(Elememt) : 새로운 기능을 추가할 클래스는 accpet 인터페이스를 상속받아 방문자(자기자신을 전달하여)에게 위임합니다.

🖥️ 코드

아래의 해더파일은 방문자와 수락자에 대한 인터페이스를 정의합니다.

ComputerPartVisitor는 방문자로 visit 함수를 정의하고 여러 요소들을 방문 할 수 있게 다형성으로 인터페이스를 정의합니다.

ComputerPart는 요소로 방문자에게 위임하기위한 인터페이스를 정의합니다.

#ifndef COMPOENT_INTERFACE
#define COMPOENT_INTERFACE

#include <iostream>
using namespace std;

// Forward declaration
class Computer;
class Keyboard;
class Monitor;
class Mouse;

// Visitor Interface
class ComputerPartVisitor {
public:
    virtual void visit(Computer &computer) = 0;
    virtual void visit(Keyboard &keyboard) = 0;
    virtual void visit(Monitor &monitor) = 0;
    virtual void visit(Mouse &mouse) = 0;
    virtual ~ComputerPartVisitor() {}
};

// Element Interface
class ComputerPart {
public:
    virtual void accept(ComputerPartVisitor &visitor) = 0;
    virtual ~ComputerPart() {}
};
#endif

ComputerPartDisplayVisitor는 방문자 인터페이스를 구현합니다. 요소에서 방문자에게 위임을 하여 방문자는 기능에 대한 구현을 합니다.

#include "ComponentInterface.hpp"

class ComputerPartDisplayVisitor : public ComputerPartVisitor {
public:
    void visit(Computer &computer) override {
        cout << "Displaying Computer." << endl;
    }

    void visit(Keyboard &keyboard) override {
        cout << "Displaying Keyboard." << endl;
    }

    void visit(Monitor &monitor) override {
        cout << "Displaying Monitor." << endl;
    }

    void visit(Mouse &mouse) override {
        cout << "Displaying Mouse." << endl;
    }
};

새로운 기능을 추가하기위해 ComputerPart 인터페이스를 구현합니다. 구현방법은 동작을 방문자에게 위임합니다. 여기서 자기자신의 인스턴스를 전달합니다.

#include "ComponentInterface.hpp";

class Keyboard : public ComputerPart {
public:
    void accept(ComputerPartVisitor &visitor) override {
        visitor.visit(*this);
    }
};

class Monitor : public ComputerPart {
public:
    void accept(ComputerPartVisitor &visitor) override {
        visitor.visit(*this);
    }
};

class Mouse : public ComputerPart {
public:
    void accept(ComputerPartVisitor &visitor) override {
        visitor.visit(*this);
    }
};

class Computer : public ComputerPart {
    ComputerPart *parts[3];

public:
    Computer() {
        parts[0] = new Keyboard();
        parts[1] = new Monitor();
        parts[2] = new Mouse();
    }

    void accept(ComputerPartVisitor &visitor) override {
        for (auto &part : parts) {
            part->accept(visitor);
        }
        visitor.visit(*this);
    }

    ~Computer() {
        for (auto &part : parts) {
            delete part;
        }
    }
};

ComputerPartVisitor를 생성하여 accept 하여 KeyBoard, Mouse, Monitor, Computer에 대한 기능을 클래스의 변경없이 위임하여 동작하도록 만들 수 있습니다.

#include "ConcreateComponent.hpp"
#include "ConcreateVisitor.hpp"

int main() {
    ComputerPart *computer = new Computer();
    ComputerPartVisitor *visitor = new ComputerPartDisplayVisitor();

    computer->accept(*visitor);

    delete visitor;
    delete computer;

    return 0;
}

💁 장/단점

장점 :

  • 개방/폐쇄 원칙. 당신은 다른 클래스를 변경하지 않으면서 해당 클래스의 객체와 작동할 수 있는 새로운 행동을 도입할 수 있습니다.
  • 단일 책임 원칙. 같은 행동의 여러 버전을 같은 클래스로 이동할 수 있습니다.
  • 비지터 객체는 다양한 객체들과 작업하면서 유용한 정보를 축적할 수 있습니다. 이것은 객체 트리와 같은 복잡한 객체 구조를 순회하여 이 구조의 각 객체에 비지터 패턴을 적용하려는 경우에 유용할 수 있습니다.

단점:

  • 클래스가 요소 계층구조에 추가되거나 제거될 때마다 모든 비지터를 업데이트해야 합니다.
  • 비지터들은 함께 작업해야 하는 요소들의 비공개 필드들 및 메서드들에 접근하기 위해 필요한 권한이 부족할 수 있습니다.
728x90

'디자인패턴' 카테고리의 다른 글

상태(State) 패턴  (0) 2024.03.16