상태값을 가지는 클래스와 상태값을 이용한 비즈니스 로직을 가지는 클래스를 분리하여
로직 클래스가 상태값 클래스를 방문하면서 로직을 수행하가는 패턴
1. 코드
Element: 상태값 클래스의 부모 클래스
public interface Element {
void accept(Visitor visitor); // 로직 클래스의 로직을 실행시키는 메소드
}
File: 상태값 클래스1
public class File implements Element {
private String name;
private int size;
public File(String name, int size) {
this.name = name;
this.size = size;
}
public String getName() {
return this.name;
}
public int getSize() {
return this.size;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
Directory: 상태값 클래스2
public class Directory implements Element {
private String name;
List<Element> list;
public Directory(String name) {
this.name = name;
}
public void add(Element element) {
if (list == null) {
list = new ArrayList<>();
}
list.add(element);
}
public String getName() {
return this.name;
}
public List<Element> getList() {
return list;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
Visitor: 로직 클래스의 부모 클래스
public interface Visitor {
void visit(File file);
void visit(Directory directory);
}
PrintNameVisitor: 로직 클래스1 (directory를 탐색하며 경로 출력 하는 로직)
public class PrintNameVisitor implements Visitor {
private String curLocationName = "";
@Override
public void visit(File file) {
System.out.println(curLocationName + "/" + file.getName());
}
@Override
public void visit(Directory directory) {
String tmp = curLocationName;
curLocationName += "/" + directory.getName();
System.out.println(curLocationName);
List<Element> elements = directory.getList();
for (Element element : elements) {
element.accept(this);
}
curLocationName = tmp;
}
}
SizeCalculateVisitor: 로직 클래스2 (현재 디렉토리안에 있는 파일들의 size 합을 구하는 로직)
public class SizeCalculateVisitor implements Visitor {
private int size = 0;
public int getSize() {
return this.size;
}
@Override
public void visit(File file) {
size += file.getSize();
}
@Override
public void visit(Directory directory) {
List<Element> elements = directory.getList();
for (Element element : elements) {
element.accept(this);
}
}
}
Main: 테스트 클래스
public class Main {
public static void main(String[] args) {
Directory root = new Directory("root");
Directory dir1 = new Directory("dir1");
Directory dir2 = new Directory("dir2");
Directory dir3 = new Directory("dir3");
root.add(dir1);
root.add(dir2);
root.add(dir3);
dir1.add(new File("dir1_f1", 10));
dir1.add(new File("dir1_f2", 20));
Directory dir1_1 = new Directory("dir1_1");
dir1.add(dir1_1);
dir1_1.add(new File("dir1_1_f1", 23));
dir2.add(new File("dir2_f1", 22));
dir3.add(new File("dir3_f1", 2));
dir3.add(new File("dir3_f2", 2));
root.accept(new PrintNameVisitor());
SizeCalculateVisitor sizeCalculateVisitor = new SizeCalculateVisitor();
root.accept(sizeCalculateVisitor);
System.out.println(sizeCalculateVisitor.getSize());
}
}
2. 특징
- 로직 클래스의 visit 메소드와 상태값 클래스의 accept 메소드가 서로를 호출 함
- 상태값과 로직을 분리
- 새로운 로직이 필요할 때 로직 클래스를 따로 생성만 하면 됨 (상태값 클래스는 변하지 않음)
-> OCP 원칙 (확장은 열고 기존 클래스의 수정은 닫는 원칙)
- 상태값 클래스가 늘어나는 상황이 예상 되는 상황에서는 비효율일 수 있음
-> 새로운 상태값 클래스에 대해 모든 로직 클래스에서 대응이 필요하다
- 여러 형태의 상태값 클래스에서 공통 로직이 필요할 때 로직 클래스에서 공통 메소드를 사용하면 됨
-> 로직이 상태값 클래스에 있으면 공통 로직을 빼기 애매해짐
- 로직이 각 상태값 클래스에서 어떻게 실행되는지 한눈에 파악 가능 / 모든 로직 파악은 한눈에 파악 불가
3. 해당 패턴을 고려해볼만한 상황
- 여러 상태가 존재하고 그 상태들의 관계가 복잡한 경우 (복잡한 상태와 로직을 분리)
- 여러 상태값들이 가지는 로직 중 공통적으로 사용해야하는 로직이 있는 경우
- 상태값의 변동이 없고 로직의 추가가 예상되는 경우
'디자인 패턴 > Java언어로 배우는 디자인패턴 입문 책 정리' 카테고리의 다른 글
Facade 패턴 (0) | 2021.02.12 |
---|---|
Chain Of Responsibility 패턴 (0) | 2021.02.11 |
Decorator 패턴 (0) | 2021.01.30 |
Composite 패턴 (0) | 2021.01.24 |
strategy 패턴 (0) | 2021.01.24 |