Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- java
- spring
- kotlin
- jpa
- Exception
- Thymeleaf
- 스프링 핵심 원리
- http
- 그리디
- 자바
- QueryDSL
- Servlet
- transaction
- 알고리즘
- 스프링 핵심 기능
- Android
- 백준
- 김영한
- springdatajpa
- db
- SpringBoot
- pointcut
- JDBC
- 인프런
- Proxy
- Greedy
- JPQL
- Spring Boot
- AOP
- 스프링
Archives
- Today
- Total
개발자되기 프로젝트
구조 안을 돌아다니며 처리 - Visitor 본문
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
'Java > 디자인 패턴' 카테고리의 다른 글
낭비를 없애기 - Flyweight Pattern (0) | 2021.11.15 |
---|---|
구조 안을 돌아다니며 처리 - Chain of Responsibility (0) | 2021.11.15 |
구조안을 돌아다니며 처리 - Iterator Pattern (0) | 2021.11.13 |
복잡한 연결을 단순하게 - Mediator Pattern (0) | 2021.11.12 |
복잡한 연결을 단순하게 - Facade Pattern (0) | 2021.11.12 |
Comments