방문자 패턴을 사용하는 사용자는 ConcreteVisitor 클래스의 객체를 생성하고 객체 구조에 따라서 각 원소를 방문하며 순회한다.
방문자가 구성 원소들을 방문할 때, 구성 원소는 해당 클래스의 Visitor 연산을 호출한다. 이 원소들은 자신을 Visitor 연산에 필요한 인자로 제공하여 방문자 자신의 상태에 접근할 수 있도록 한다.
이중 디스패치 (Double Dispatch)를 사용하는 패턴
방문자 패턴과 이중 디스패치의 관계
방문자 패턴에서 이중 디스패치가 중요한 이유는, 방문자 패턴의 핵심이 객체의 타입에 따라 적절한 연산을 수행하는 것이기 때문. 이중 디스패치를 통해, 방문자 패턴은 다음을 가능하게 한다.
- 첫 번째 디스패치: 객체 구조에서 요소가 accept(visitor:) 메서드를 호출할 때, 호출된 요소 객체의 런타임 타입에 따라 적절한 방문자 메서드가 호출.
- 두 번째 디스패치: accept(visitor:) 메서드 내에서, 전달된 방문자 객체의 타입에 따라 적절한 방문 메서드가 다시 호출.
클래스가 요소 계층구조에 추가되거나 제거될 때마다 모든 비지터를 업데이트해야 함.
// Visitor 프로토콜 정의
protocol Visitor {
func visitCircle(_ circle: Circle)
func visitRectangle(_ rectangle: Rectangle)
}
// Shape 프로토콜 정의
protocol Shape {
func accept(visitor: Visitor)
}
// 구체적인 도형 클래스들
class Circle: Shape {
func accept(visitor: Visitor) {
visitor.visitCircle(self)
}
// Circle의 구체적인 로직들 (예: 반지름, 면적 계산 등)
var radius: Double
init(radius: Double) {
self.radius = radius
}
}
class Rectangle: Shape {
func accept(visitor: Visitor) {
visitor.visitRectangle(self)
}
// Rectangle의 구체적인 로직들 (예: 너비, 높이, 면적 계산 등)
var width: Double
var height: Double
init(width: Double, height: Double) {
self.width = width
self.height = height
}
}
// 구체적인 방문자 클래스
class AreaCalculator: Visitor {
func visitCircle(_ circle: Circle) {
let area = 3.14 * circle.radius * circle.radius
print("Area of Circle: \(area)")
}
func visitRectangle(_ rectangle: Rectangle) {
let area = rectangle.width * rectangle.height
print("Area of Rectangle: \(area)")
}
}
// 사용 예시
let circle = Circle(radius: 5.0)
let rectangle = Rectangle(width: 4.0, height: 6.0)
let calculator = AreaCalculator()
circle.accept(visitor: calculator) // Output: Area of Circle: 78.5
rectangle.accept(visitor: calculator) // Output: Area of Rectangle: 24.0