본문 바로가기

IT/디자인 패턴(Design Pattern)

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

1) 개요

방문자 패턴은 로직을 객체 구조에서 분리 시키는 디자인 패턴입니다.

비슷한 종류의 객체들을 가진 그룹에서 작업을 수행해야 할 때 주로 사용되는 패턴입니다.


- 장점

1. 로직이 추가/변경되면 그 로직이 포함된 클래스를 찾아가며 변경하지 않고 방문자 객체만 변경하면 됩니다.


- 단점

1. 방문자 인터펭스에 구현해야할 로직이 많으면 유지보수에 어려움이 있습니다.

2. visit 메소드의 return 타입을 각각 파악하고 있어야 합니다.


2) UML


- Client : 명령을 보냅니다.

- Visitor : 명령을 수행하기 위해 필요한 메소드를 정의하는 인터페이스입니다.

- ConcreteVisitor : 명령을 수행하는 메소드를 구현합니다.

- Element : Visit를 사용할 수 있는지 확인하는 accept 메소드를 정의하는 인터페이스입니다.

- ConcreteElement : Visitable에서 정의된 accept 메소드를 구현하며 Visitor객체는 이 객체를 통하여 명령이 전달됩니다.


3) 예제


1
2
3
4
public interface ItemElement {
    public int accept(ShoppingCartVisitor visitor);
}
 
cs


1
2
3
4
5
public interface ShoppingCartVisitor {
    int visit(Book book);
    int visit(Fruit fruit);
}
 
cs


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Book implements ItemElement{
 
    private int price;
    private String isbnNumber;
    
    public Book(int cost, String isbn) {
        this.price = cost;
        this.isbnNumber = isbn;
    }
    
    public int getPrice() {
        return price;
    }
    
    public String getIsbnNumber() {
        return isbnNumber;
    }
    
    @Override
    public int accept(ShoppingCartVisitor visitor) {
        return visitor.visit(this);
    }
 
}
cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Fruit implements ItemElement{
    private int pricePerKg;
    private int weight;
    private String name;
    
    public Fruit(int priceKg, int weight, String name) {
        this.pricePerKg = priceKg;
        this.weight = weight;
        this.name = name;
    }
    
    public String getName() {
        return name;
    }
    
    public int getPricePerKg() {
        return pricePerKg;
    }
    
    public int getWeight() {
        return weight;
    }
    
    @Override
    public int accept(ShoppingCartVisitor visitor) {
        return visitor.visit(this);
    }
 
}
 
cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class ShoppingCartVisitorImpl implements ShoppingCartVisitor{
 
    @Override
    public int visit(Book book) {
        int cost = 0;
        
        if(book.getPrice() > 50) cost = book.getPrice()-5;
        else cost = book.getPrice();
        
        System.out.println("Book ISBN : " + book.getIsbnNumber() + " cost = " + cost);
        return cost;
    }
 
    @Override
    public int visit(Fruit fruit) {
        int cost = fruit.getPricePerKg() * fruit.getWeight();
        System.out.println(fruit.getName() + " cost = " + cost);
        return cost;
    }
 
}
cs


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class ShoppingCartClient {
    public static void main(String[] args) {
        ItemElement[] items = new ItemElement[] {new Book(20"What is Justice?"), new Book(100"DesignPattern"),
                                                 new Fruit(102"Banana"), new Fruit(55"Apple")};
        
        int total = calculatePrice(items);
        System.out.println("Total Cost = " + total);
    }
    
    private static int calculatePrice(ItemElement[] items) {
        ShoppingCartVisitor visitor = new ShoppingCartVisitorImpl();
        int sum = 0;
        
        for (ItemElement item : items) {
            sum = sum + item.accept(visitor);
        }
        
        return sum;
    }
}
 
cs