apple/DesignPattern, Architecture
Swift 디자인패턴 State Pattern (상태 패턴)
lgvv
2022. 5. 30. 19:26
Swift 디자인패턴 State Pattern (상태 패턴)
State Pattern은 객체의 내부 상태에 따라 동작이 변경되도록 설계하는 행동 패턴.
히스토리
- 2022-05-30: 디자인 패턴 스터디 정리
- 2024-11-27: 포스팅 글 재정리 및 조금 더 실용적인 예제 코드로 변경
State Pattern
상태 패턴을 사용하면 객체의 상태를 별도의 상태 클래스로 추상화하고, 객체는 자신의 상태를 바꾸면서 동작을 동적으로 변경할 수 있음
- Context: 상태를 관리하고, 현재 상태에 따라 행동을 위임하는 클래스
- State: 공통 인터페이스 또는 추상 클래스 정의 모든 Concrete 클래스는 해당 인터페이스 구현
- Concrete State: State 인터페이스를 구현한 클래스로 상태별 고유 동작 저의
상태 패턴을 적용할 수 있는 상황
- if - else 혹은 switch 문에서 처리하는 내부 구문이 길어질 경우 분리
- 플레이어의 상태에 따라 각 상태를 런타임에 관리하고, 불필요한 상태에 대한 부작용을 막기 위함
상태 패턴 장점
- 행동의 캡슐화: 상태별 동작은 개별 객체에 정의하여 코드 가독성과 유지 보수성 높임
- 상태 전환의 유연성: 객체 내부 상태를 쉽게 변경하고, 새로운 상태를 추가하거나 수정하기 쉬움
- 조건문 감소: if - else 및 switch를 제거하고 객체로 동작을 위임
상태 패턴 단점
- 상태 객체 수 증가: 각 상태마다 별도의 객체를 만들어야 해서 클래스가 증가
- 간단한 논리라면 오히려 더 복잡해질 수 있음
- 구현에 따라 결합도가 높아질 수 있음
코드 예제
플레이어에서 상태를 관리하는 State를 분리하는 간단한 예제
- 플레이어는 복잡해서 각 상태에 따라서 퍼널 로그 수집, 각 상태에 따른 별도의 정책을 적용 받을 수 있어서 상태 처리가 복잡
import SwiftUI
import Foundation
/// 상태 인터페이스
protocol PlayerState {
var player: VideoPlayer { get }
func play()
func pause()
func stop()
}
/// Context
final class VideoPlayer {
private var state: PlayerState? = nil
init() {}
func setState(_ state: PlayerState) {
self.state = state
}
func play() {
state?.play()
}
func pause() {
state?.pause()
}
func stop() {
state?.stop()
}
}
// MARK: - Concrete States
class PlayingState: PlayerState {
var player: VideoPlayer
init(player: VideoPlayer) {
self.player = player
}
func play() {
print("Already playing.")
}
func pause() {
print("Pausing playback...")
player.setState(PausedState(player: player))
}
func stop() {
print("Stopping playback...")
player.setState(StoppedState(player: player))
}
}
class PausedState: PlayerState {
var player: VideoPlayer
init(player: VideoPlayer) {
self.player = player
}
func play() {
print("Resuming playback...")
player.setState(PlayingState(player: player))
}
func pause() {
print("Already paused.")
}
func stop() {
print("Stopping playback...")
player.setState(StoppedState(player: player))
}
}
class StoppedState: PlayerState {
var player: VideoPlayer
init(player: VideoPlayer) {
self.player = player
}
func play() {
print("Starting playback...")
player.setState(PlayingState(player: player))
}
func pause() {
print("Cannot pause. Player is stopped.")
}
func stop() {
print("Already stopped.")
}
}
private struct ContentView: View {
var body: some View {
Button("Execute") {
let player = VideoPlayer()
player.setState(PlayingState(player: player))
player.play()
player.pause()
player.stop()
player.play()
}
}
}
#Preview {
ContentView()
}
(참고)
https://www.raywenderlich.com/books/design-patterns-by-tutorials/v3.0/chapters/15-state-pattern
https://refactoring.guru/ko/design-patterns/state