Visitor

최완식·2023년 2월 21일
0

Design Patterns

목록 보기
26/26
post-thumbnail

GoF의 디자인 패턴, 전략 패턴에 대해 알아본다.

해당 글은, 다음의 코드를 기반으로 이해하는 것이 편리합니다.

핵심 요약

  • 데이터 구조와 데이터 처리를 분리해주는 패턴
  • 데이터 처리 방식을 기존의 소스 코드 변경 없이 새로운 클래스 추가만으로 확장 가능함
  • 데이터 구조는 Composite Pattern을 사용함

예시

Code

main

//
//  main.swift
//  Visitor
//
//  Created by Choiwansik on 2023/02/21.
//

import Foundation

internal func main() {
    let list1 = ItemList()
    list1.add(unit: Item(value: 10))
    list1.add(unit: Item(value: 20))
    list1.add(unit: Item(value: 30))

    let list2 = ItemList()
    list2.add(unit: Item(value: 40))
    list2.add(unit: Item(value: 50))
    list2.add(unit: list1)

    let list3 = ItemList()
    list3.add(unit: Item(value: 60))
    list3.add(unit: list1)
    list3.add(unit: list2)

    let sum = SumVisitor()
    let avg = AvgVisitor()

    list1.accept(visitor: sum)
    list1.accept(visitor: avg)
    print("List1 SUM: \(sum.value)")
    print("List1 AVG: \(avg.value)")

    list2.accept(visitor: sum)
    list2.accept(visitor: avg)
    print("List2 SUM: \(sum.value)")
    print("List2 AVG: \(avg.value)")

    list3.accept(visitor: sum)
    list3.accept(visitor: avg)
    print("List3 SUM: \(sum.value)")
    print("List3 AVG: \(avg.value)")

}

main()

List1 SUM: 60
List1 AVG: 20.0
List2 SUM: 210
List2 AVG: 26.25
List3 SUM: 480
List3 AVG: 28.235294117647058

Unit

//
//  Unit.swift
//  Visitor
//
//  Created by Choiwansik on 2023/02/21.
//

import Foundation

internal protocol Unit {

    func accept(visitor: Visitor)
    
}

Item

//
//  Item.swift
//  Visitor
//
//  Created by Choiwansik on 2023/02/21.
//

import Foundation

internal struct Item: Unit {

    internal let value: Int

    internal func accept(visitor: Visitor) {
        visitor.visit(unit: self)
    }
    
}

ItemList

//
//  ItemList.swift
//  Visitor
//
//  Created by Choiwansik on 2023/02/21.
//

import Foundation

internal class ItemList: Unit {

    internal func add(unit: Unit) {
        self.list.append(unit)
    }

    internal func accept(visitor: Visitor) {
        self.list.forEach { visitor.visit(unit: $0) }
    }

    private(set) var list = Array<Unit>()

}

Visitor

//
//  Visitor.swift
//  Visitor
//
//  Created by Choiwansik on 2023/02/21.
//

import Foundation

internal protocol Visitor {

    func visit(unit: Unit)
    
}

SumVisitor

//
//  SumVisitor.swift
//  Visitor
//
//  Created by Choiwansik on 2023/02/21.
//

import Foundation

internal class SumVisitor: Visitor {

    internal var value: Int {
        self.sum
    }

    internal func visit(unit: Unit) {
        if let item = unit as? Item {
            sum += item.value
        } else {
            unit.accept(visitor: self)
        }
    }

    private(set) var sum: Int = .zero

}

AvgVisitor

//
//  AvgVisitor.swift
//  Visitor
//
//  Created by Choiwansik on 2023/02/21.
//

import Foundation

internal class AvgVisitor: Visitor {

    internal var value: Double {
        Double(self.sum) / Double(self.count)
    }

    internal func visit(unit: Unit) {
        if let item = unit as? Item {
            self.sum += item.value
            self.count += 1
        } else {
            unit.accept(visitor: self)
        }
    }

    private(set) var sum = 0
    private(set) var count = 0
}

활용성

  • 객체 구조와 처리로직을 분리하여 유지보수와 확장성을 향상시킬 수 있다.
  • 객체 구조는 동일하나, 연산을 추가하고 싶을 때

생각해볼 점

  • 코드의 복잡성이 증가될 수 있다.
  • 구조를 바꾸지 않고 새로운 동작을 추가할 수 있으나, 객체 요소가 Visitor 인터페이스에 의존하게 된다.

Reference

profile
Goal, Plan, Execute.

0개의 댓글