/ 设计模式 / 状态模式 / Swift Swift 状态模式讲解和代码示例 状态是一种行为设计模式, 让你能在一个对象的内部状态变化时改变其行为。 该模式将与状态相关的行为抽取到独立的状态类中, 让原对象将工作委派给这些类的实例, 而不是自行进行处理。 进一步了解状态模式 导航 简介 概念示例 Example Output 真实世界示例 Example Output 复杂度: 流行度: 使用示例: 在 Swift 语言中, 状态模式通常被用于将基于 switch语句的大型状态机转换为对象。 识别方法: 状态模式可通过受外部控制且能根据对象状态改变行为的方法来识别。 以下示例可在 Swift Playgrounds 上使用。 感谢 Alejandro Mohamad 创建了Playground版本。 概念示例 真实世界示例 概念示例 本例说明了状态设计模式的结构并重点回答了下面的问题: 它由哪些类组成? 这些类扮演了哪些角色? 模式中的各个元素会以何种方式相互关联? 了解该模式的结构后, 你可以更容易地理解下面基于真实世界的 Swift 应用案例。 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: class { 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... 概念示例 真实世界示例