방문자(Visitor) 패턴
방문자(Visitor) 패턴은 객체 구조(컬렉션 등)의 요소에 새로운 기능을 추가하고 싶을 때 사용하는 행동 디자인 패턴입니다.
이 패턴에서는 **방문자 객체(Visitor)**가 객체 구조의 요소를 방문하며 각기 다른 작업을 수행합니다.
특히, 객체의 구조를 변경하지 않고도 새로운 동작을 추가할 수 있는 점이 특징입니다.
문제점: 객체 구조를 변경하지 않고 새로운 기능을 추가해야 하는 경우
예를 들어, 다양한 종류의 도형(Circle, Rectangle)에 대해 면적 계산, 색상 변경 등 새로운 기능을 추가해야 한다고 가정합니다. 이때 각 도형 클래스에 기능을 추가하면 클래스의 복잡도가 증가합니다. 또한, 매번 기능을 추가할 때마다 도형 클래스를 수정해야 하므로 유지보수가 어렵습니다.
class Circle {
public void draw() {
System.out.println("Drawing a Circle");
}
public void calculateArea() {
System.out.println("Calculating area of Circle");
}
}
class Rectangle {
public void draw() {
System.out.println("Drawing a Rectangle");
}
public void calculateArea() {
System.out.println("Calculating area of Rectangle");
}
}
문제점
- 코드 중복: 각 클래스에 비슷한 기능이 반복됩니다.
- 유연성 부족: 새로운 기능이 필요할 때마다 모든 도형 클래스를 수정해야 합니다.
- OCP(Open-Closed Principle) 위반: 기존 클래스를 수정하지 않고 기능을 확장해야 하는 원칙이 지켜지지 않습니다.
해결 방법: 방문자 패턴 적용
방문자 패턴에서는 객체의 구조와 기능을 분리합니다.
즉, 객체에 직접 기능을 추가하는 대신, Visitor 객체가 각 요소를 방문하면서 필요한 작업을 수행합니다.
이로써 기존 클래스에 대한 수정 없이 새로운 기능을 쉽게 추가할 수 있습니다.
Visitor 인터페이스 정의
interface ShapeVisitor {
void visit(Circle circle);
void visit(Rectangle rectangle);
}
Visitor 구현체 구현
class AreaCalculator implements ShapeVisitor {
@Override
public void visit(Circle circle) {
System.out.println("Calculating area of Circle");
}
@Override
public void visit(Rectangle rectangle) {
System.out.println("Calculating area of Rectangle");
}
}
class ColorChanger implements ShapeVisitor {
@Override
public void visit(Circle circle) {
System.out.println("Changing color of Circle");
}
@Override
public void visit(Rectangle rectangle) {
System.out.println("Changing color of Rectangle");
}
}
도형 클래스 구현
interface Shape {
void accept(ShapeVisitor visitor);
}
class Circle implements Shape {
@Override
public void accept(ShapeVisitor visitor) {
visitor.visit(this);
}
}
class Rectangle implements Shape {
@Override
public void accept(ShapeVisitor visitor) {
visitor.visit(this);
}
}
클라이언트 코드
public class Main {
public static void main(String[] args) {
Shape circle = new Circle();
Shape rectangle = new Rectangle();
ShapeVisitor areaCalculator = new AreaCalculator();
ShapeVisitor colorChanger = new ColorChanger();
// 면적 계산 기능 수행
circle.accept(areaCalculator); // Calculating area of Circle
rectangle.accept(areaCalculator); // Calculating area of Rectangle
// 색상 변경 기능 수행
circle.accept(colorChanger); // Changing color of Circle
rectangle.accept(colorChanger); // Changing color of Rectangle
}
}
방문자 패턴의 동작 원리
- 객체 구조는 방문자를 받아들이는 메서드(
accept
)를 구현합니다. - Visitor 객체는 객체 구조의 각 요소를 방문하며 해당 요소에 적합한 작업을 수행합니다.
- 새로운 작업이 필요하면 Visitor 인터페이스를 확장하여 쉽게 구현할 수 있습니다.
방문자 패턴의 장단점
장점
- 기능 확장 용이: 객체의 구조를 변경하지 않고도 새로운 기능을 쉽게 추가할 수 있습니다.
- OCP 준수: 기존 코드를 수정하지 않고 기능을 확장합니다.
- 코드 분리: 객체의 데이터와 기능을 분리하여 유지보수성이 향상됩니다.
단점
- 객체 구조의 변경이 어렵다: 객체 구조가 자주 변경되면, 모든 Visitor 클래스를 수정해야 합니다.
- 복잡성 증가: 객체와 Visitor 간의 상호작용이 많아지면 코드가 복잡해질 수 있습니다.
- 클래스 수 증가: 각 기능마다 별도의 Visitor 클래스를 만들어야 하므로 클래스 수가 많아질 수 있습니다.
결론
Visitor 패턴은 객체의 구조와 기능을 분리하여 기존 클래스를 수정하지 않고 기능을 확장할 수 있는 유용한 패턴입니다. 특히, 다양한 객체 구조에 대해 일관된 작업을 수행해야 할 때 유용하며, 컴파일러, 파일 시스템 탐색, 게임 개발 등에서 자주 사용됩니다.
하지만 객체 구조가 자주 바뀌거나 코드가 단순할 경우 Visitor 패턴의 복잡성이 오히려 부담이 될 수 있으므로 상황에 따라 적절하게 사용해야 합니다.