개요

 데이터 구조 안에 많은 요소가 저장되 있고, 각 요소를 가지고 어떤 작업을 해야할 때 그 작업은 어디어 기술해 놓아야 할까? 쉽게 생각한다면 요소 자체에 저장해놓겠지만, 작업이 다양해 진다면 요소마다 새로운 작업을 추가해야 할 것이다.  Visitor 패턴은 작업(=처리)를 요소와 분리하기 위한 패턴으로 특정한 작업(=처리)이 기술된 방문자라는 역할을 만들어서 테이터 구조를 이루는 요소를 적절한 방문자로 처리하는 기법이다. 요소들은 처리방법에 대해 몰라도 되고 방문자를 받아들여서 작업을 하면 상세한 처리 방법은 받아들인 방문자가 가지고 있는 것이다.

 

 

역할

Visitor 역할

  데이터 구조를 이루는 구체적요소(ConcreteElment)를 가지고 실행하는 visit()  메소드를 선언한다. 예제에서는 Visitor 클래스.

 

ConcreteVisitor 역할

  Visiotr역할에서 선언한 visit() 메소드를 각각 필요한 역할에 따라 구현한다. 예제에서는 ListVisitor 클래스.

 

Element 역할

  Visitor가 방문할 대상이 되는 역할로, Visitor를 받아들이는 accept() 메서드를 선언한다. 예제에서는 Element 인터페이스.

 

ConcreteElement 역할

  Element 역할의 인터페이스를 구현하는 역할 예제에서는 File클래스나 Directory 클래스.

 

ObjectStructure 역할

  Element의 집합을 취급하는 역할. 예제에서는 Directory 클래스가 ConcreteElment역할과 함께 ObjectStructure 역할을 한다.

 

이점 및 유의사항

 Visitor패턴의 가장 큰 특징은 바로 데이터 구조와 처리를 분리하는 것이다. 데이터의 구조는 요소를 정리하거나 요소간을 연결해주는 역할이라면 처리는 데이터 구조를 이용한 어떤 작업 과정에 대한 역할로 분리된다.

 결과적으로 ConcreteElement역할을 하는 (예제에서 File, Directory 클래스) 대상을 처리 과정에서 특정한 부품으로 독립시킬 수 있다. 이를 통해 새로운 처리과정이 필요하거나 수정해야 할 경우 ConcreteElement는 손대지 않고도 수정이 가능한 것이다.

 

 

예제 코드

//Visitor 역할
package visitor2;

public abstract class Visitor {
	public abstract void visit(File file);
	public abstract void visit(Directory directory);
}


//ConcreteVisitor 역할
package visitor2;

import java.util.Iterator;

public class ListVisitor extends Visitor{
	private String currentdir = "";
	public void visit(File file) {
		System.out.println(currentdir + "/" + file);
	}
	
	public void visit(Directory directory) {
		System.out.println(currentdir + "/" + directory);
		String savedir = currentdir;
		currentdir = currentdir + "/" + directory.getName();
		Iterator<Entry> it = directory.iterator();
		
		while(it.hasNext()) {
			Entry entry = (Entry)it.next();
			entry.accept(this);
		}
		currentdir = savedir;
	}
	
	
	
}


//Element 역할1
package visitor2;

public interface Element {
	public abstract void accept(Visitor v);
}


//Element 역할2
package visitor2;

import java.util.Iterator;

public abstract class Entry implements Element{
	public abstract String getName();
	public abstract int getSize();
	
	public Entry add(Entry entry) throws FileTreatmentException {
		throw new FileTreatmentException();
	}
	
	public Iterator<Entry> iterator() throws FileTreatmentException {
		throw new FileTreatmentException();
	}
	
	public String toString() {
		return getName() + " (" + getSize() + ")";
	}
}


//ConcreteElement 역할1
package visitor2;

public class File extends Entry{
	private String name;
	private int size;
	
	public File(String name, int size) {
		this.name = name;
		this.size = size;
	}
	
	public String getName() {
		return name;
	}
	
	public int getSize() {
		return size;
	}
	
	public void accept(Visitor v) {
		v.visit(this);
	}
}


//ConcreteElement 역할2
//ObjectStructure 역할
package visitor2;

import java.util.ArrayList;
import java.util.Iterator;

public class Directory extends Entry{
	private String name;
	private ArrayList<Entry> dir = new ArrayList<>();
	
	public Directory(String name) {
		this.name = name;
	}
	
	public String getName() {
		return name;
	}
	
	public int getSize() {
		int size = 0;
		Iterator<Entry> it = dir.iterator();
		
		while(it.hasNext()) {
			Entry entry = (Entry)it.next();
			size += entry.getSize();
		}
		return size;
	}
	
	
	public Entry add(Entry entry) {
		dir.add(entry);
		return this;
	}
	
	public Iterator<Entry> iterator() {
		return dir.iterator();
	}
	
	public void accept(Visitor v) {
		v.visit(this);
	}
	
}


//Tester
package visitor2;

public class VisitorTester {
	public static void main(String[] args) {
		try {
			System.out.println("Making root entries");
			Directory rootdir = new Directory("root");
			Directory bindir = new Directory("bin");
			Directory tmpdir = new Directory("tmp");
			Directory usrdir = new Directory("usr");
			
			rootdir.add(bindir);
			rootdir.add(tmpdir);
			rootdir.add(usrdir);
			bindir.add(new File("vi", 10000));
			bindir.add(new File("latex", 20000));
			rootdir.accept(new ListVisitor());
			
			System.out.println("");
			System.out.println("Making user entries..");
			Directory Kim = new Directory("Kim");
			Directory Lee = new Directory("Lee");
			Directory Park = new Directory("Park");
			
			usrdir.add(Kim);
			usrdir.add(Lee);
			usrdir.add(Park);
			Kim.add(new File("diart.html", 100));
			Kim.add(new File("Composite.java", 200));
			Lee.add(new File("memo.txt", 300));
			Park.add(new File("game.doc", 400));
			Park.add(new File("junk", 500));
			rootdir.accept(new ListVisitor());
			
		} catch(FileTreatmentException e) {
			e.printStackTrace();
		}
	}
}


//기타
package visitor2;

public class FileTreatmentException extends RuntimeException{
	public FileTreatmentException() {
		
	}
	
	public FileTreatmentException(String msg) {
		super(msg);
	}
}

'디자인 패턴' 카테고리의 다른 글

15. Facade 패턴  (0) 2020.12.26
14. Chain of Responsibility 패턴  (0) 2020.12.22
12. Decorator 패턴  (0) 2020.12.16
11. Composite 패턴  (0) 2020.12.15
10. Strategy 패턴  (0) 2020.12.09

+ Recent posts