JAVA

[디자인패턴] Visitor 패턴

gom1n 2022. 6. 16. 15:32

Visitor 패턴

데이터구조를 돌아다니면서 처리한다.

데이터의 구조와 처리를 분리한다.

데이터 구조를 돌아다니는 '방문자'를 정의해서, 이 방문자가 '처리'를 담당하도록 한다.

데이터 구조는, 문을 두드리는 '방문자'를 받아들이기만 하면 된다.

 

 

FileTreatmentException 예외처리

File 엔트리에 무언가를 추가(add)하고자 할 때 발생되는 예외

 

더블 디스패치 (Double Dispatch)

Visitor와 Acceptor는 서로 대응관계에 있으며, 서로가 서로를 호출한다.


ConcreteVisitor 역할의 추가는 간단하다.

구체적인 처리는 ConcreteVisitor 역할에 맡길 수 있고, 그 처리를 위한 ConcreteElement 역할을 수정할 필요는 전혀 없기 때문이다.

 

ConcreteElement 역할의 추가는 곤란하다.

만약 새로운 역할을 추가하였을 때에, Visitor 클래스에는 메소드를 또 만들 필요성이 생긴다. 그리고 하위 클래스에 그 메소드를 구현해야한다.


연습문제

 

13-1) 이 장의 예제 프로그램에 추가할 클래스로 FileFindVisitor 클래스를 작성하십시오. 이 클래스는 지정된 확장자의 파일을 모으는 것으로 가정합니다.

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

public class FileFindVisitor extends Visitor {
    private String filetype;
    private ArrayList found = new ArrayList();
    public FileFindVisitor(String filetype) {           // '.txt'와 같이 확장자에 .을 붙여 지정
        this.filetype = filetype;
    }
    public Iterator getFoundFiles() {                 // 발견한 파일을 얻는다.
        return found.iterator();
    }
    public void visit(File file) {                      // 파일을 방문할 때 호출
        if (file.getName().endsWith(filetype)) {
            found.add(file);
        }
    }
    public void visit(Directory directory) {            // 디렉터리를 방문할 때 호출
        Iterator it = directory.iterator();
        while (it.hasNext()) {
            Entry entry = (Entry)it.next();
            entry.accept(this);
        }
    }
}

 

13-2) 예제 프로그램의 Directory 클래스의 getSize 클래스에서는 '사이즈를 얻는' 처리를 실행합니다. '사이즈를 얻는' 처리를 실행하는 SizeVisitor 클래스를 도입해서 Directory 클래스의 getSize 메소드를 다시 기술하십시오.

 

SizeVisitor.java

import java.util.Iterator;

public class SizeVisitor extends Visitor {
    private int size = 0;
    public int getSize() {
        return size;
    }
    public void visit(File file) {
        size += file.getSize();
    }
    public void visit(Directory directory) {
        Iterator it = directory.iterator();
        while (it.hasNext()) {
            Entry entry = (Entry)it.next();
            entry.accept(this);
        }
    }
}

 

13-3) java,util.ArrayList에 Element 인터페이스의 기능을 가진 ElementArrayList 클래스를 작성하십시오. ElementArrayList의 인스턴스에 대해서는 Directory나 File의 인스턴스를 add할 수 있고, ListVisitor의 인스턴스를 accept할 수 있도록 합니다. 

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

class ElementArrayList extends ArrayList implements Element {
    public void accept(Visitor v) {
        Iterator it = iterator();
        while (it.hasNext()) {
            Element e = (Element)it.next();
            e.accept(v);
        }
    }
}

 

 

13-4) final 선언되어 있는 클래스는 하위 클래스를 만들 수 없습니다. 예를 들어 String 클래스는 final 클래스로 선언되어있기 때문에 다음과 같이 MyString 클래스를 정의할 수 없습니다.

// 컴파일 에러가 뜬다.
class MyString extends String {
	...
}

이와 같이 String 클래스는 확장에 관한 The Open-Closed Principle을 위반하고 있지만, 사실은 위반할만한 정당한 이유가 있습니다. 그 이유는 무엇입니까?

 

--> 효율적이기 때문. 

String 클래스는 문자열을 다루는 기본적인 클래스로 중요한 역할을 가진다.

따라서, java 언어처리계에서는 String 클래스가 '확장되지 않는다.'는 전제아래 처리속도에 관한 최적화 및 메모리 양에 관한 최적화를 실행하고 있다.