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

koline·2023년 9월 12일
0

디자인패턴

목록 보기
24/24

방문자 패턴


각 클래스 데이터 구조로부터 처리 기능을 분리하여 별도의 클래스를 만들어 놓고 해당 클래스의 메서드가 각 클래스를 돌아다니며 특정 작업을 수행하도록 만드는 패턴으로, 객체의 구조는 변경하지 않으면서 기능만 따로 추가하거나 확장할 때 사용하는 패턴이다.

이렇게 분리를 하면 구조를 수정하지 않고도 실질적으로 새로운 동작을 기존의 객체 구조에 추가할 수 있게 되기 때문에 개방-폐쇄 원칙을 적용하는 방법의 하나이다.



구조


  1. Visitor: 데이터 구조 내 각각의 구체적인 요소에 visit메소드를 선언한다.
  2. ConcreteVisitor: Visitor 인터페이스를 구현하고, ConcreteAccepter 역할을 처리한다.
  3. Acceptor: Acceptor는 Visitor역할이 방문할 곳을 나타내는 역할을 하고 있다.
    방문자를 받아들이는 accept메소드를 선언한다.
  4. ConcreteAcceptor: Acceptor 인터페이스를 구현하는 역할을 한다.
  5. ObjectStructure: Acceptor역할의 집할을 취급하는 역할을 한다.



구현


// Element.java (Acceptor)
public interface Element {
    void accept(Visitor visitor);
}

// Cart.java (ObjectStructure)
public class Cart implements Element {

    List<Element> cart = new ArrayList<>();

    public Cart() {
        cart.add(new Ramen("진라면 매운맛"));
        cart.add(new Snack("다이제 씬"));
    }

    @Override
    public void accept(Visitor visitor) {
        System.out.println("[Cart] Cart is ready...");
        visitor.visit(this);

        for (Element el : cart) {
            el.accept(visitor);
        }
    }
}

// Ramen.java (ConcreteAcceptor)
public class Ramen implements Element {

    private String name;

    public Ramen(String name) {
        this.name = name;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
        System.out.println("[Ramen] " + name + " has been picked up...");
    }
}

// Snack.java (ConcreteAcceptor)
public class Snack implements Element {

    private String name;

    public Snack(String name) {
        this.name = name;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
        System.out.println("[Snack] " + name + " has been picked up...");
    }
}

// Visitor.java (Visitor)
public interface Visitor {
    void visit(Cart cart);
    void visit(Snack snack);
    void visit(Ramen ramen);
}

// Shopper.java (ConcreteVisitor)
public class Shopper implements Visitor {

    @Override
    public void visit(Cart cart) {
        System.out.println("[Shopper] I am using a cart...");
    }

    @Override
    public void visit(Snack snack) {
        System.out.println("[Shopper] I am picking up a snack...");
    }

    @Override
    public void visit(Ramen ramen) {
        System.out.println("[Shopper] I am picking up a ramen...");
    }
}

// Client.java (Client)
public class Client{
    public static void main(String[] args) {
        Shopper shopper = new Shopper();
        Cart cart = new Cart();
        cart.accept(shopper);
    }
}

// 실행 결과
[Cart] Cart is ready...
[Shopper] I am using a cart...
[Ramen] 진라면 매운맛 has been picked up...
[Shopper] I am picking up a ramen...
[Snack] 다이제 씬 has been picked up...
[Shopper] I am picking up a snack...




목적

  1. 자료 구조(데이터)와 자료 구조를 처리하는 로직(알고리즘)을 분리해야 할때 유용합니다.
  2. 데이터 구조보다 알고리즘이 더 자주 바뀔때 유용합니다.

장점

  1. 작업 대상(방문 공간)과 작업 항목(방문 공간에서 하는 일)을 분리시킬 수 있습니다.
  2. 작업 대상(방문 공간)은 단지 데이터를 가지고 있는 자료구조로 만들고, 작업 주체(방문자)는 visit() 메서드에서 작업 대상을 입력받아 작업 항목을 처리하면 됩니다.
  3. 즉, 데이터와 알고리즘이 분리되어 데이터의 독립성을 높여줍니다.
  4. 작업 대상의 입장에서는 accept()로 인터페이스를 통일시키기 때문에 사용자에게 동일한 인터페이스를 제공할 수 있습니다.

단점

  1. 새로운 작업 대상(방문 공간)이 추가될때마다 작업 주체(방문자)도 이에 대한 로직을 추가해야 합니다.
  2. 두 객체(방문자와 방문 공간)의 결합도가 높아집니다.



참고


[디자인패턴] 디자인패턴이란? - 생성패턴, 구조패턴, 행위패턴

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

Visitor 패턴

profile
개발공부를해보자

0개의 댓글