'JAVA 언어로 배우는 디자인 패턴 입문'의 내용을 정리한 것입니다.
Visitor 패턴
- 데이터 구조 안에 많은 요소가 저장되어 있고 어떠한 처리를 한다고 하자.
- 이때 그 처리 코드는 어디에 써야 할까? 일반적으로 클래스 안에 쓸 것이다.
- 하지만 그 처리가 한 종류가 아니라면 매번 데이터 구조 클래스를 수정해야 한다.
- Visitor 패턴은 데이터 구조와 처리를 분리한다.
- 데이터 구조 안을 돌아다니는 주체인 '방문자'르 나타내는 클래스를 준비하고 그 클래스에 처리를 맡긴다.
- 새로운 처리를 추가하고 싶을 때는 새로운 '방문자'를 만들면 된다.
예제 프로그램
Visitor 클래스
public abstract class Visitor {
public abstract void visit(File file);
public abstract void visit(Directory directory);
}
Element 인터페이스
public interface Element {
void accept(Visitor visitor);
}
Entry 클래스
public abstract class Entry implements Element {
public abstract String getName();
public abstract int getSize();
@Override
public String toString() {
return getName() + " (" + getSize() + ")";
}
}
File 클래스
public class File extends Entry {
private String name;
private int size;
public File(String name, int size) {
this.name = name;
this.size = size;
}
@Override
public String getName() {
return name;
}
@Override
public int getSize() {
return size;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
Directory 클래스
public class Directory extends Entry implements Iterable<Entry> {
private String name;
private List<Entry> directory = new ArrayList<>();
public Directory(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public int getSize() {
int size = 0;
for (Entry entry: directory) {
size += entry.getSize();
}
return size;
}
public Entry add(Entry entry) {
directory.add(entry);
return this;
}
@Override
public Iterator<Entry> iterator() {
return directory.iterator();
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
ListVisitor 클래스
public class ListVisitor extends Visitor {
private String currentdir = "";
@Override
public void visit(File file) {
System.out.println(currentdir + "/" + file);
}
@Override
public void visit(Directory directory) {
System.out.println(currentdir + "/" + directory);
String savedir = currentdir;
currentdir = currentdir + "/" + directory.getName();
for (Entry entry: directory) {
entry.accept(this);
}
currentdir = savedir;
}
}
Main 클래스
public class Main {
public static void main(String[] args) {
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();
}
}
실행 결과
Making root entries...
/root (30000)
/root/bin (30000)
/root/bin/vi (10000)
/root/bin/latex (20000)
/root/tmp (0)
/root/usr (0)
Visotor(방문자) 역
- 데이터 구조의 구체적인 요소마다 'xxxx를 방문했다'는 visit(xxxx) 메소드를 선언한다.
- Visitor 클래스가 이 역할
ConcreteVisitor(구체적인 방문자) 역
- Visitor 인터페이스를 구현
- ListVisitor 클래스가 이 역할
Element(요소) 역
- Visitor가 방문할 곳을 나타내며, 방문자를 받아들이는 accept 메소드를 선언
- Element 인터페이스가 이 역할
ObjectStructure(오브젝트 구조) 역
- Element 집합을 다룬다.
- Directory 클래스가 이 역할
정리
- 인터페이스를 통해 호출이 되고 실제 처리를 하위 클래스에서 결정하는 일이 두번 실행된다. 이를 더블 디스패치라고 한다.
- Visitor 패턴의 목적은 처리를 데이터 구조와 분리하는 것이다.
- 이는 기능이 확장이 되는 경우에 가능한 기존 클래스가 수정되지 않고 새로운 클래스 추가로 확장하기 위함인데 이런 원칙을 OCP(Open Closed Principle)라고 한다.
반응형