Notice
Recent Posts
Recent Comments
Link
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

개발자되기 프로젝트

구조 안을 돌아다니며 처리 - Visitor 본문

Java/디자인 패턴

구조 안을 돌아다니며 처리 - Visitor

Seung__ 2021. 11. 13. 19:08

1. Visitor Pattern???


  • 구조 안을 돌아다니며 일은 함 "방문자"
  • 객체 구조의 요소들이 수행해야할 기능을 모아 둔 패턴
  • 각 객체 요소를 변경하지 않고, 기능을 추가할 수 있음.
  • 데이터의 구조와 처리를 분리하여, 처리하는 부분을 객체로 만든다. 해당 객체가 각 데이터 구조를 돌아가니며 오퍼레이션일 수행되도록 함.
  • 각 데이터는 이러한 visitor를 accept 함.
  • .

 

2. 의도 (Intent)와 동기(Motivation)


  • 추상구문 트리의 예
  • 문법적 처리를 위해 각 노드마다 수행해야 하는 기능을 따로 정의 해야함
    • 아래의 경우 Node를 상속받아 각각 구현하는 경우.
    • 각각 구현하기 때문에, 유사한 오퍼레이션이 여러 객체에 분산되어 있는 것이 특징.
    • 주요 기능은 TypeCheck(), GenerateCode()

 

  • 위와 같은 경우는 유사한 오퍼레이션들이 여러 객체에 분산되어 있어서 디버깅이 어렵고, 가독성이 떨어짐
  • 각 클래스로부터 관련한 클래스를 모아서 Visitor를 별도로 만들어줌
    • Visitor의 역할은 각 클래스(노드, VariableRefNode, AssignmentNode)를 방문 하는 역할
    • Visitor를 상속받아 주요 기능인 TypeCheck(), GenerateCode()를 위한 Visitor를 각각 생성.
      • 즉 기능 별로 Visitor를 생성함.
    • TypeCheckingVisitor, CodeGeneratingVisitor가 각 클래스(노드)를 방문하여 기능을 실행.

.

  • 각 노드 클래스는 visitor가 방문하면 accept 하여 해당 노드에 대한 기능이 수행되도록 함

 

 

3. Class diagram


 

 

4. 객체 협력 (collaborations)


  • Visitor

각 객체에서 수행해야 할 기능을 선언한다.

메서드의 이름은 요청을 받을 객체를 명시한다.

  • ConcreteVisitor

Visitor 클래스에 선언된 메서드를 구현한다.

각 메서드는 객체에 해당하는 알고리즘을 구현한다.

  • Element

Visitor가 수행될 수 있는 Accept() 메서드를 선언한다.

  • ConcreteElement

매개변수로 Visitor를 받아주는 Accept()메서드를 구현한다.

  • ConcreteAggregate

해당하는 ConcreteIteratir의 인스턴스를 반환하도록 Iterator 생성 인터페이스를 구현

 

 

5. 중요한 결론 (consequence)


  • 여러 요소에 대해 유사한 기능의 메서드를 한곳에 모아서 관리하게 되고, 여러 클래스에 걸친 메서드를 추가하기 용이함
  • 각 클래스에 대한 기능이 자주 변경되거나 알고리즘의 변화가 많을때 사용하는것이 효율적임
  • 새로운 요소 클래스를 추가하게 되면 그에 해당되는 기능을 모든 Visitor에서 구현해하 함
  • 따라서 요소의 변화가 거의 없고 기능의 추가 삭제가 자주 발생할때 사용하는것이 좋음
    • visitor를 변경, 추가하면 기능의 변경 확장이 가능.
  • 각 객체의 오퍼레이션이 외부에 노출되는 것이므로 객체지향 프로그래밍의 캡슐화 전략에 위배
  • 주로 Composite 패턴에서 자주 사용될 수 있음
    • Composite, container와 leaf를 동일한 클래스로 핸들링.
    • 각 클래스 별로 유사한 기능을 관리하면 복잡함.... Visitor를 통해 유사한 기능을 모아서 관리하면 편함.
    • https://bsh-developer.tistory.com/601
  • Visitor는 여로 요소에 대한 유사한 기능 별로 생성한다.
  • 클래스가 자꾸 변할 때 사용하면 안됨. 이미 각 클래스가 제공하는 기능이 Visitor로 세분화 되어있음.
    만약 새로운 클래스가 추가되면 모든 Visitor 수정해야함. 

 

 

 

6. 예제


  • 보통의 경우라면 File, Directory 클래스에서 현재 경로를 출력하는 메서드를 구현해야 한다.
  • 두 클래스 모두 경로를 출력하는 기능이 유사하므로 클래스에서 구현하는 메서드를 Visitor로 빼낸다.
public interface Acceptor {

    public abstract void accept(Visitor v);
}
public abstract class Entry implements Acceptor {
    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() + ")";
    }


}
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 void accept(Visitor v) {
        v.visitFile(this);
    }

    @Override
    public String getName() {
        return  name;
    }

    @Override
    public int getSize() {
        return size;
    }
}
public class Directory extends Entry{

    private String name;
    private ArrayList<Entry> dir = new ArrayList<>();

    public Directory(String name) {
        this.name = name;
    }

    @Override
    public void accept(Visitor v) {
        v.visitDirectory(this);
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public int getSize() {
       int size = 0;
        Iterator<Entry> iterator = dir.iterator();
        while (iterator.hasNext()){
            Entry entry = iterator.next();
            size += entry.getSize();
        }
        return  size;
    }

    public Entry add(Entry entry){
        dir.add(entry);
        return this;
    }

    public Iterator<Entry> iterator(){
        return dir.iterator();
    }

}
public abstract class Visitor {

    public abstract void visitFile(File file);
    public abstract void visitDirectory(Directory file);

}
import java.util.Iterator;

public class ListVisitor extends Visitor{

    private String currentDir = "";
    @Override
    public void visitFile(File file) {
        System.out.println(currentDir + "/" + file);
    }

    @Override
    public void visitDirectory(Directory directory) {
        System.out.println(currentDir + "/"+ directory );
        String saveDir = currentDir;
        currentDir = currentDir + "/" + directory.getName();
        Iterator<Entry> iterator = directory.iterator();
        while (iterator.hasNext()){
            Entry entry = iterator.next();
            entry.accept(this);
        }
        currentDir = saveDir;
    }
}
public class Test {

    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 Kang = new Directory("Kang");
            usrdir.add(Kim);
            usrdir.add(Lee);
            usrdir.add(Kang);
            Kim.add(new File("diary.html", 100));
            Kim.add(new File("Composite.java", 200));
            Lee.add(new File("memo.tex", 300));
            Kang.add(new File("game.doc", 400));
            Kang.add(new File("junk.mail", 500));
            rootdir.accept(new ListVisitor());
        } catch (FileTreatmentException e) {
            e.printStackTrace();
        }
    }

}
public class FileTreatmentException extends RuntimeException {
    public FileTreatmentException() {
    }
    public FileTreatmentException(String msg) {
        super(msg);
    }
}
Making root entries...
/root(30000)
/root/bin(30000)
/root/bin/vi(10000)
/root/bin/latex(20000)
/root/tmp(0)
/root/usr(0)

Making user entries...
/root(31500)
/root/bin(30000)
/root/bin/vi(10000)
/root/bin/latex(20000)
/root/tmp(0)
/root/usr(1500)
/root/usr/Kim(300)
/root/usr/Kim/diary.html(100)
/root/usr/Kim/Composite.java(200)
/root/usr/Lee(300)
/root/usr/Lee/memo.tex(300)
/root/usr/Kang(900)
/root/usr/Kang/game.doc(400)
/root/usr/Kang/junk.mail(500)

 

 

7. GitHub : 211113 Visitor Pattern


 

GitHub - bsh6463/designPattern

Contribute to bsh6463/designPattern development by creating an account on GitHub.

github.com

 

Comments