[디자인 패턴] Visitor 패턴

이멀젼씨

·

2020. 3. 17. 14:19

- 정의

 

객체의 구조와 기능을 분리시켜 구조를 수정하지 않고도 새로운 동작을 기존의 객체 구조에 추가할 수 있게 만드는 패턴이다

 

- UML

1. Visitable인터페이스를 생성하고 Visitor타입을 받는 accept메서드를 선언한다

2. Visitable인터페이스를 구현하는 Office와 House클래스를 생성하고 Visitor타입을 받아 visit메서드를 실행하는 accept메서드를 구현한다. 각각의 name을 사무실과 가정집으로 설정한다

3. Visotr인터페이스를 생성하고 House와 Office타입을 받는 visit메소드를 선언한다

4. Visitor인터페이스를 구현하는 ComputerRepairman과 TVRepairman클래스를 생성하고 House와 Office타입을 받아 수리행위를 출력하는 visit메서드를 구현한다

 

- 코드

 

Visitable interface

public interface Visitable {
	public void accept(Visitor visitor);
}

Office class

public class Office implements Visitable{
	private String name = "사무실";
	
	@Override
	public void accept(Visitor visitor) {
		visitor.visit(this);	
	}
	
	public String getName() {
		return name;
	}
	
	public void setName(String name) {
		this.name = name;
	}
		
}

House class

public class House implements Visitable {
	private String name = "가정집";
	
	@Override
	public void accept(Visitor visitor) {
		visitor.visit(this);	
	}
	
	public String getName() {
		return name;
	}
	
	public void setName(String name) {
		this.name = name;
	}
	
}

Visitor interface

public interface Visitor {
	public void visit(House house);
	public void visit(Office office);
}

TVRepairman class

public class TVRepairman implements Visitor{
	@Override
	public void visit(Office office) {
		System.out.println(office.getName()+"의 TV를 점검&수리 합니다");
	}
	@Override
	public void visit(House house) {
		System.out.println(house.getName()+"의 TV를 점검&수리 합니다");
	}
}

ComputerRepairman class

public class ComputerRepairman implements Visitor{
	@Override
	public void visit(Office office) {
		System.out.println(office.getName()+"의 컴퓨터를 점검&수리 합니다");
	}
	@Override
	public void visit(House house) {
		System.out.println(house.getName()+"의 컴퓨터를 점검&수리 합니다");
	}
}

Main class

import java.util.ArrayList;

public class Main {
	public static void main(String[] args) {	
		// 여러개의 방문 지점(사무실, 가정집)에 대해 동일한 비지터(티비 수리기사)가 처리
		ArrayList<Visitable> places = new ArrayList<>();
		places.add(new House());
		places.add(new Office());
		
		TVRepairman tvRepairman = new TVRepairman();
		
		for(Visitable place : places) {
			place.accept(tvRepairman);
		}
		
		System.out.println("======================");
		
		// 한개의 방문 지점(사무실)에 대해 여러 비지터(티비 수리기사, 컴퓨터 수리기사) 처리
		ArrayList<Visitor> visitors = new ArrayList<>();
		visitors.add(new TVRepairman());
		visitors.add(new ComputerRepairman());
		
		Office office = new Office();
		
		for(Visitor visitor : visitors) {
			office.accept(visitor);
		}

	}
}

- 설명

비지터패턴을 특정 장소에 방문하여 수리하는 사람을 예로 들어보자

 

방문을 온 사람이 수행하는 행동은 동일해야 한다.

컴퓨터 수리기사면 컴퓨터를 점검 및 수리하고, TV 수리기사면 TV를 점검 및 수리해야 한다.

방문을 온 사람은 행동을 수행하는 visitor이다

 

visitor가 방문하는 장소는 visitor의 방문을 받아들여야한다(accept메소드). 방문을 받아들인 뒤에 visitor가 하려는 행동을 하게 만들어야 한다(visit메서드).

 

main의 첫 번째 예시를 보면 여러 장소(사무실, 가정집)에 대해 TV 수리기사가 방문했을 때 사무실과 가정집의 TV를 점검하고 수리하였다

 

두 번째 예시는 한 방문 장소(사무실)에 대해 여러 수리기사(TV 수리기사, 컴퓨터 수리기사)가 방문했을 때 사무실의 TV와 컴퓨터를 점검한다는 것을 알 수 있다

 

이렇듯 visitor를 상속받는 클래스는 방문자로서의 역할(기능)만 수행하고, visitable을 상속받는 클래스는 기존 구조에서 방문자의 역할을 분리시켜 둔 것이다

 

즉, 실제 로직을 가지고 있는 객체(Visitor)가 로직을 적용할 객체(Visitable) 방문하면서 로직을 실행하게 된다고 생각하면 된다

 

이로서 단순한 객체 구조를 다룰 수 있게 하고 기능의 확장에 용이하게 만든다

 

하지만 최초 구조를 설정하는 것이 까다롭고, 대상이 될 객체(여기선 방문할 장소)가 추가될 때마다 비지터 인터페이스에 메서드를 추가해 주어야 한다.

또한 객체 간의 결합도가 높고, 비지터가 객체의 속성 값을 직접 제어하므로 캡슐화가 약해진다

 

 

 

 

 

장점 : 단순한 객체 구조를 다룰 수 있게 만들고, 기능 확장이 용이하다

 

 

단점 : 객체간의 결합도가 높고, Visitor객체가 속성값을 직접 제어하므로 캡슐화가 약해진다