스위프트로 작성된 상태
상태는 객체의 내부 상태가 변경될 때 해당 객체가 행동을 변경할 수 있도록 하는 행동 디자인 패턴입니다.
패턴은 상태 관련 행동들을 별도의 상태 클래스들로 추출하며 또 원래 객체가 자체적으로 작동하는 대신 위에 언급된 클래스들에 작업을 위임하도록 강제합니다.
복잡도:
인기도:
사용 사례들: 상태 패턴은 일반적으로 스위프터 언어에서 대규모 switch 기반 상태 머신들을 객체들로 변환하는 데 사용됩니다.
식별: 객체들의 상태에 따라 행동을 변경하는 메서드들이 있으면 패턴은 상태 패턴으로 초기 식별될 수 있으며 이 상태가 상태 객체들 자체를 포함하여 다른 객체들에 의해 제어되거나 대체될 수 있으면 해당 패턴은 상태 패턴입니다.
개념적인 예시
이 예시는 상태 디자인 패턴의 구조를 보여주고 다음 질문에 중점을 둡니다:
- 패턴은 어떤 클래스들로 구성되어 있나요?
 - 이 클래스들은 어떤 역할을 하나요?
 - 패턴의 요소들은 어떻게 서로 연관되어 있나요?
 
이 패턴의 구조를 배우면 실제 스위프트 사용 사례를 기반으로 하는 다음 예시를 더욱 쉽게 이해할 수 있을 것입니다.
Example.swift: 개념적인 예시
import XCTest
/// The Context defines the interface of interest to clients. It also maintains
/// a reference to an instance of a State subclass, which represents the current
/// state of the Context.
class Context {
    /// A reference to the current state of the Context.
    private var state: State
    init(_ state: State) {
        self.state = state
        transitionTo(state: state)
    }
    /// The Context allows changing the State object at runtime.
    func transitionTo(state: State) {
        print("Context: Transition to " + String(describing: state))
        self.state = state
        self.state.update(context: self)
    }
    /// The Context delegates part of its behavior to the current State object.
    func request1() {
        state.handle1()
    }
    func request2() {
        state.handle2()
    }
}
/// The base State class declares methods that all Concrete State should
/// implement and also provides a backreference to the Context object,
/// associated with the State. This backreference can be used by States to
/// transition the Context to another State.
protocol State: AnyObject {
    func update(context: Context)
    func handle1()
    func handle2()
}
class BaseState: State {
    private(set) weak var context: Context?
    func update(context: Context) {
        self.context = context
    }
    func handle1() {}
    func handle2() {}
}
/// Concrete States implement various behaviors, associated with a state of the
/// Context.
class ConcreteStateA: BaseState {
    override func handle1() {
        print("ConcreteStateA handles request1.")
        print("ConcreteStateA wants to change the state of the context.\n")
        context?.transitionTo(state: ConcreteStateB())
    }
    override func handle2() {
        print("ConcreteStateA handles request2.\n")
    }
}
class ConcreteStateB: BaseState {
    override func handle1() {
        print("ConcreteStateB handles request1.\n")
    }
    override func handle2() {
        print("ConcreteStateB handles request2.")
        print("ConcreteStateB wants to change the state of the context.\n")
        context?.transitionTo(state: ConcreteStateA())
    }
}
/// Let's see how it all works together.
class StateConceptual: XCTestCase {
    func test() {
        let context = Context(ConcreteStateA())
        context.request1()
        context.request2()
    }
}
Output.txt: 실행 결과
Context: Transition to StateConceptual.ConcreteStateA
ConcreteStateA handles request1.
ConcreteStateA wants to change the state of the context.
Context: Transition to StateConceptual.ConcreteStateB
ConcreteStateB handles request2.
ConcreteStateB wants to change the state of the context.
Context: Transition to StateConceptual.ConcreteStateA
실제 사례 예시
Example.swift: 실제 사례 예시
import XCTest
class StateRealWorld: XCTestCase {
    func test() {
        print("Client: I'm starting working with a location tracker")
        let tracker = LocationTracker()
        print()
        tracker.startTracking()
        print()
        tracker.pauseTracking(for: 2)
        print()
        tracker.makeCheckIn()
        print()
        tracker.findMyChildren()
        print()
        tracker.stopTracking()
    }
}
class LocationTracker {
    /// Location tracking is enabled by default
    private lazy var trackingState: TrackingState = EnabledTrackingState(tracker: self)
    func startTracking() {
        trackingState.startTracking()
    }
    func stopTracking() {
        trackingState.stopTracking()
    }
    func pauseTracking(for time: TimeInterval) {
        trackingState.pauseTracking(for: time)
    }
    func makeCheckIn() {
        trackingState.makeCheckIn()
    }
    func findMyChildren() {
        trackingState.findMyChildren()
    }
    func update(state: TrackingState) {
        trackingState = state
    }
}
protocol TrackingState {
    func startTracking()
    func stopTracking()
    func pauseTracking(for time: TimeInterval)
    func makeCheckIn()
    func findMyChildren()
}
class EnabledTrackingState: TrackingState {
    private weak var tracker: LocationTracker?
    init(tracker: LocationTracker?) {
        self.tracker = tracker
    }
    func startTracking() {
        print("EnabledTrackingState: startTracking is invoked")
        print("EnabledTrackingState: tracking location....1")
        print("EnabledTrackingState: tracking location....2")
        print("EnabledTrackingState: tracking location....3")
    }
    func stopTracking() {
        print("EnabledTrackingState: Received 'stop tracking'")
        print("EnabledTrackingState: Changing state to 'disabled'...")
        tracker?.update(state: DisabledTrackingState(tracker: tracker))
        tracker?.stopTracking()
    }
    func pauseTracking(for time: TimeInterval) {
        print("EnabledTrackingState: Received 'pause tracking' for \(time) seconds")
        print("EnabledTrackingState: Changing state to 'disabled'...")
        tracker?.update(state: DisabledTrackingState(tracker: tracker))
        tracker?.pauseTracking(for: time)
    }
    func makeCheckIn() {
        print("EnabledTrackingState: performing check-in at the current location")
    }
    func findMyChildren() {
        print("EnabledTrackingState: searching for children...")
    }
}
class DisabledTrackingState: TrackingState {
    private weak var tracker: LocationTracker?
    init(tracker: LocationTracker?) {
        self.tracker = tracker
    }
    func startTracking() {
        print("DisabledTrackingState: Received 'start tracking'")
        print("DisabledTrackingState: Changing state to 'enabled'...")
        tracker?.update(state: EnabledTrackingState(tracker: tracker))
    }
    func pauseTracking(for time: TimeInterval) {
        print("DisabledTrackingState: Pause tracking for \(time) seconds")
        for i in 0...Int(time) {
            print("DisabledTrackingState: pause...\(i)")
        }
        print("DisabledTrackingState: Time is over")
        print("DisabledTrackingState: Returing to 'enabled state'...\n")
        self.tracker?.update(state: EnabledTrackingState(tracker: self.tracker))
        self.tracker?.startTracking()
    }
    func stopTracking() {
        print("DisabledTrackingState: Received 'stop tracking'")
        print("DisabledTrackingState: Do nothing...")
    }
    func makeCheckIn() {
        print("DisabledTrackingState: Received 'make check-in'")
        print("DisabledTrackingState: Changing state to 'enabled'...")
        tracker?.update(state: EnabledTrackingState(tracker: tracker))
        tracker?.makeCheckIn()
    }
    func findMyChildren() {
        print("DisabledTrackingState: Received 'find my children'")
        print("DisabledTrackingState: Changing state to 'enabled'...")
        tracker?.update(state: EnabledTrackingState(tracker: tracker))
        tracker?.findMyChildren()
    }
}
Output.txt: 실행 결과
Client: I'm starting working with a location tracker
EnabledTrackingState: startTracking is invoked
EnabledTrackingState: tracking location....1
EnabledTrackingState: tracking location....2
EnabledTrackingState: tracking location....3
EnabledTrackingState: Received 'pause tracking' for 2.0 seconds
EnabledTrackingState: Changing state to 'disabled'...
DisabledTrackingState: Pause tracking for 2.0 seconds
DisabledTrackingState: pause...0
DisabledTrackingState: pause...1
DisabledTrackingState: pause...2
DisabledTrackingState: Time is over
DisabledTrackingState: Returing to 'enabled state'...
EnabledTrackingState: startTracking is invoked
EnabledTrackingState: tracking location....1
EnabledTrackingState: tracking location....2
EnabledTrackingState: tracking location....3
EnabledTrackingState: performing check-in at the current location
EnabledTrackingState: searching for children...
EnabledTrackingState: Received 'stop tracking'
EnabledTrackingState: Changing state to 'disabled'...
DisabledTrackingState: Received 'stop tracking'
DisabledTrackingState: Do nothing...