본문 바로가기
까망 동네/디자인 패턴

[디자인패턴] 방문자 패턴 (Visitor Pattern)

by 까망 하르방 2023. 11. 13.
반응형

🎈 방문자 패턴 (Visitor Pattern)

객체 구조를 변경하지 않고 객체 연산을 확장할 수 있는 패턴

• 디자인 패턴 중 행위 패턴에 해당

 

💻 디자인 패턴(Design Pattern)이란?

👨‍💻 디자인 패턴(Design Pattern)이란? • SW 개발 방법 중에서도 구조적인 문제 해결에 목적을 둔다. • 알고리즘과 같이 특정 문제를 해결하는 Logic 형태보다는 특정 상황에 적용할 수 있는 방

zoosso.tistory.com


 

[예제 코드]

#include <iostream>
#include <vector>

class Book;
class Magazine;

class Visitor {
public:
    virtual void visit(Book& book) = 0;
    virtual void visit(Magazine& magazine) = 0;
};

class Visitable {
public:
    virtual void accept(Visitor& visitor) = 0;
};

class Book : public Visitable {
public:
    Book(double price) : price(price) {}


    double getPrice() const {
        return price;
    }

    void accept(Visitor& visitor) override {
        visitor.visit(*this);
    }

private:
    double price;
};

class Magazine : public Visitable {
public:
    Magazine(double price) : price(price) {}

    double getPrice() const {
        return price;
    }

    void accept(Visitor& visitor) override {
        visitor.visit(*this);
    }

private:
    double price;
};

class PriceCalculator : public Visitor {
public:
    PriceCalculator() : total(0.0) {}

    void visit(Book& book) override {
        total += book.getPrice();
    }

    void visit(Magazine& magazine) override {
        total += magazine.getPrice();
    }

    double getTotalPrice() const {
        return total;
    }

private:
    double total;
};

int main() {
    std::vector<Visitable*> items;
    items.push_back(new Book(20.0));
    items.push_back(new Magazine(5.0));
    items.push_back(new Book(15.0));

    PriceCalculator calculator;
   
    for (Visitable* item : items) {
        item->accept(calculator);      
    }

    std::cout << "[원가] Total price: $" << calculator.getTotalPrice() << std::endl;

    return 0;
}

 

방문자 패턴 예시 결과

 

Book 및 Magazine 클래스에서 가격을 가져와서

PriceCalculator를 통해 총 가격을 계산한다.

 

Visitor 패턴을 사용하면 기존 클래스를 수정하지 않고

새로운 연산을 쉽게 추가할 수 있다.

 

 

[예제 코드]

특정 %{percent} 할인율 가격 확인하는 방문자 Class 추가

#include <iostream>
#include <vector>

class Book;
class Magazine;

class Visitor {
public:
    virtual void visit(Book& book) = 0;
    virtual void visit(Magazine& magazine) = 0;
};

class Visitable {
public:
    virtual void accept(Visitor& visitor) = 0;
};


class Book : public Visitable {
public:
    Book(double price) : price(price) {}

    double getPrice() const {
        return price;
    }

    void accept(Visitor& visitor) override {
        visitor.visit(*this);
    }

private:
    double price;
};


class Magazine : public Visitable {
public:
    Magazine(double price) : price(price) {}

    double getPrice() const {
        return price;
    }

    void accept(Visitor& visitor) override {
        visitor.visit(*this);
    }

private:
    double price;
};


class PriceCalculator : public Visitor {
public:
    PriceCalculator() : total(0.0) {}

    void visit(Book& book) override {
        total += book.getPrice();
    }

    void visit(Magazine& magazine) override {
        total += magazine.getPrice();
    }

    double getTotalPrice() const {
        return total;
    }

private:
    double total;
};


class PriceDiscount : public Visitor {
public:
    PriceDiscount() {}

    void visit(Book& book) override {
        std::cout << "[" << percent << "% 할인] 책: " \
            << getDiscount(book.getPrice()) << std::endl;
    }

    void visit(Magazine& magazine) override {
        std::cout << "[" << percent << "% 할인] 잡지: " \
            << getDiscount(magazine.getPrice()) << std::endl;
    }

    double getDiscount(double price) {
        return price - (price * percent / 100);
    }

    void setPercent(int _percent) {
        percent = _percent;
    }

private:
    int percent;
};


int main() {
    std::vector<Visitable*> items;
    items.push_back(new Book(20.0));
    items.push_back(new Magazine(5.0));
    items.push_back(new Book(15.0));

    PriceCalculator calculator;
    for (Visitable* item : items) {
        item->accept(calculator);      
    }
    std::cout << "[원가] Total price: $" << calculator.getTotalPrice() << std::endl;

    PriceDiscount discountor;
    discountor.setPercent(50);
    for (Visitable* item : items) {
        item->accept(discountor);
    }

    return 0;
}

 

방문자 패턴 예시 결과

 

Visitor 인터페이스를 정의하여 새로운 연산(로직) 추가

마찬가지로 accept 메서드를 통해 방문자를 받아들일 수 있게 한다.


 

🎈 방문자 패턴 장단점

+ 연산의 분리: 객체 구조와 연산을 독립적으로 확장 및 변경할 수 있다.

+ 단일 책임 원칙: 각 클래스는 자신의 상태와 연산에만 집중할 수 있다

 

- 결합도 증가: 객체 구조의 각 요소가 모든 방문자를 인식해야 하므로,

 클래스 간의 결합도가 증가할 수 있다.

- 유지 보수: 새로운 요소나 방문자를 추가할 때,

 모든 방문자 클래스에 새로운 메서드 구현 필요하다.

반응형

댓글