도서 요약 / / 2023. 1. 15. 21:15

[JAVA 언어로 배우는 디자인 패턴 입문] Visitor 패턴

'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)라고 한다.



반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유