비지터 패턴

정선호·2023년 5월 31일
0

Design Patterns

목록 보기
24/24

비지터 패턴으로 플레이어 직업 분화
비지터 패턴으로 발사체 구분

비지터 패턴

위키피디아 - 비지터 패턴
설명 및 스도코드

  • 알고리즘을 객체 구조에서 분리시키는 디자인 패턴

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

비지터 패턴의 구조

  • 비지터 인터페이스(Visitor Interface)

    • 객체 구조의 구상 요소들을 인수들로 사용할 수 있는 비지터 메서드들의 집합을 선언
    • 이러한 메서드들은 (앱이 오버로딩을 지원하는 언어로 작성된 경우) 같은 이름을 가질 수 있지만 그들의 매개변수들의 유형은 달라야 한다
  • 구상 비지터(Concrete Visitor)

    • 다양한 구상 요소 클래스들에 맞춤으로 작성된 같은 행동들의 여러 버전을 구현
  • 요소 인터페이스(Element Interface)

    • 비지터를 '수락'하는 메서드를 선언
    • 이 메서드에는 비지터 인터페이스 유형으로 선언된 하나의 매개변수가 있어야 한다
  • 구상 요소(Concrete Element)

    • 반드시 수락 메서드를 구현해야 한다
    • 이 메서드의 목적은 호출을 현재 요소 클래스에 해당하는 적절한 비지터 메서드로 리다이렉트하는 것
    • 기초 요소 클래스가 이 메서드를 구현하더라도 모든 자식 클래스들은 여전히 자신들의 클래스들 내에서 이 메서드를 오버라이드해야 하며 비지터 객체에 적절한 메서드를 호출해야 한다
  • 클라이언트(Client)

    • 일반적으로 컬렉션 또는 기타 복잡한 객체​(예: 복합체 트리)​를 나타낸다
    • 일반적으로 클라이언트들은 해당 컬렉션의 객체들과 어떠한 추상 인터페이스를 통해 작업하기 때문에 모든 구상 요소 클래스들을 인식하지 못한다

비지터 패턴의 적용

  • 복잡한 객체 구조​(예: 객체 트리)​의 모든 요소에 대해 작업을 수행해야 할 때 사용

    • 비지터 패턴은 비지터 객체가 모든 대상 클래스들에 해당하는 같은 작업의 여러 변형들을 구현하도록 함으로써 다양한 클래스들을 가진 여러 객체의 집합에 작업을 실행할 수 있도록 해준다
  • 비지터 패턴을 사용하여 보조 행동들의 비즈니스 로직을 정리

    • 앱의 주 클래스들의 주 작업들을 제외한 모든 다른 행동들을 비지터 클래스들의 집합으로 추출함으로써 그들이 주 작업에 더 집중하도록 만들 수 있게 해준다
  • 행동이 클래스 계층구조의 일부 클래스들에서만 의미가 있고 다른 클래스들에서는 의미가 없을 때 사용

    • 이 행동을 별도의 비지터 클래스로 추출한 후 관련 클래스들의 객체들을 수락하는 비지터 메서드들만 구현하고 나머지는 비워둔다

다른 패턴과의 관계

  • 비지터 패턴은 커맨드 패턴의 강력한 버전으로 취급할 수 있다. 비지터 패턴의 객체들은 다른 클래스들의 다양한 객체에 대한 작업을 실행할 수 있다.

  • 비지터 패턴을 사용하여 복합체 패턴 트리 전체를 대상으로 작업을 수행할 수 있다.

  • 비지터 패턴과 반복자 패턴을 함께 사용해 복잡한 데이터 구조를 순회하여 해당 구조의 요소들의 클래스들이 모두 다르더라도 이러한 요소들에 대해 어떤 작업을 실행할 수 있다.

비지터 패턴 예시

  • 요소 인터페이스

방문자를 받아들이기 위한 accept메소드를 가지고 있다

public interface Element {
    public void accept(Visitor visitor);
}
  • 구상 요소들

Cart생성자에서, 담을 물품을 추가. 카트에 추가된 물품의 개수 만큼 반복문을 돌며 해당 요소의 accept메소드를 실행

public class Cart implements Element {
	ArrayList<Element> cart = new ArrayList<>();
    
    public Cart() {
    	cart.add(new Snack());
        cart.add(new Milk());
    }

    @Override
    public void accept(Visitor visitor) {
    	System.out.println("카트가 준비되었습니다.");
        visitor.visit(this);
        
        for(Element element : cart) {
        	element.accept(visitor);
        }
    }
}
// Snack
public class Snack implements Element {
    @Override
    public void accept(Visitor visitor) {
    	System.out.println("과자가 준비되었습니다.");
        visitor.visit(this);
    }
}
// Milk
public class Milk implements Element {
    @Override
    public void accept(Visitor visitor) {
    	System.out.println("우유가 준비되었습니다.");
        visitor.visit(this);
    }
}
  • 비지터 인터페이스
// Visitor
public interface Visitor {
    public void visit(Cart cart);
    public void visit(Snack snack);
    public void visit(Milk milk);
}
  • 구상 비지터
public class Shopper implements Visitor {
	@Override
    public void visit(Cart cart) {
    	System.out.println("카트를 사용합니다.");
    }
    
    @Override
    public void visit(Snack snack) {
    	System.out.println("과자를 카트에 넣습니다.");
    }
    
    @Override
    public void visit(Milk milk) {
    	System.out.println("우유를 카트에 넣습니다.");
    }
}
  • 클라이언트
public class Client {
    public static void main(String args[]) {
        Shopper shopper = new Shopper();
        Cart cart = new Cart();
        cart.accept(shopper);
    }
}
profile
학습한 내용을 빠르게 다시 찾기 위한 저장소

0개의 댓글