Notice
Recent Posts
Recent Comments
Link
Tags
- Human Interface Guidelines
- iPad
- WWDC
- 전달인자 레이블
- lineBreakMode
- Combine+UIKit
- github
- lineBreakStrategy
- 스위프트
- LanguageGuide
- CollectionView
- TOSS
- UILabel
- Keychain
- 앱개발
- 디자인패턴
- 야곰아카데미
- orthogonalScrollingBehavior
- HIG
- DiffableDataSource
- Apple
- Accessibility
- IOS
- iTerm
- Swift
- 애플사이다
- UIKit
- Split View
- GOF
- 애플
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- Today
- Total
애플사이다의 iOS 개발 일지
[디자인 패턴] Command - 실행할 작업을 덩어리로 관리할 때 본문
GoF 디자인 패턴 중 하나인 Command 패턴을 정리했다.
- Ref : 도서 <Dive into Design Patterns>, Alexander Shvets 저
문제 상황
- Text Editor 프로그램을 개발하는 중이며, 텍스트를 저장할 Save Button을 만들었다.
Button을 상속받아 자식클래스로 Save Button을 구현한 뒤, save 기능을 추가했다. - 그런데 Save Button 뿐만 아니라 Ctrl+C 단축키로도 복사를 하고 싶고,
텍스트와 서식을 동시에 복사할 Super Save Button을 추가하고 싶어졌다. - 기존 방식으로 하면 버튼/단축키에 중복 코드가 생기고, 자식클래스 종류가 너무 많아질 수 있다는 문제점이 있다.
패턴 설명
- Command의 사전적 정의는 “명령, 명령어”이다.
Command 패턴에서는 실행할 작업을 캡슐화하여 하나의 덩어리로 취급한다.
즉, 실행할 명령 (ex. SaveCommand)을 별도 타입으로 정의해 두고,
필요한 곳 (ex. SaveButton, SaveShortcut)에 해당 명령 덩어리를 주입시켜서 활용한다. - 관심사 분리의 원칙에 따라 UI 로직과 비즈니스 로직을 분리하는게 좋다.
Command 객체는 UI 객체와 비즈니스 로직 객체 사이의 연결고리 역할을 한다.
덕분에 UI 객체가 비즈니스 로직에게 직접 작업을 요청하지 않는다. - Command 패턴은 아래 5가지 역할로 구성된다.
언뜻 복잡해보이지만 예시코드를 먼저 보면 쉽게 이해할 수 있다.
- Invoker (발송자) : 커맨드에게 일을 시킨다. 커맨드 객체의 참조를 저장할 프로퍼티를 가진다.
- Command 인터페이스 : 단일 메서드 execute()를 선언한다.
- Concrete Command : 일을 구현한다. Concrete Command를 초기화할 때,
Receiver 자체 (또는 Receiver의 메서드를 실행하는 데 필요한 매개변수)를 전달받아 프로퍼티로 가진다. - Receiver (수신자) : 실제로 작업이 실행되는 곳이다.
- Client : Concrete Command를 초기화한다.
예시 코드
// Command
protocol Command {
func execute()
}
// Concrete Command
class SaveCommand: Command {
var textEditor: TextEditor
init(textEditor: TextEditor) {
self.textEditor = textEditor
}
func execute() {
// 복사 기능을 구현
let currentText = textEditor.text
textEditor.clipboard = currentText
print("Text Copied!")
}
}
// Invoker - 커맨드에게 일을 시킴
class SaveButton {
var command: Command? // 커맨드를 저장
func setCommand(_ command: Command) {
self.command = command
}
func executeCommand() {
command?.execute()
}
}
class SaveShortcut {
var command: Command?
func setCommand(_ command: Command) {
self.command = command
}
func executeCommand() {
command?.execute()
}
}
// Receiver - 실제로 작업이 실행되는 곳
class TextEditor {
// 가정 : 복사 기능이 실행되면 -> text를 clipboard에 저장함
var text: String = ""
var clipboard: String = ""
}
// Client
let textEditor = TextEditor() // receiver
let saveCommand = SaveCommand(textEditor: textEditor)
let saveButton = SaveButton() // invoker-1
saveButton.setCommand(saveCommand)
saveButton.executeCommand() // Text Copied! 출력
let saveShortcut = SaveShortcut() // invoker-2
saveShortcut.setCommand(saveCommand) // 재사용 가능
saveShortcut.executeCommand() // Text Copied! 출력
장점
- 단일책임 원칙을 준수한다. Receiver (TextEditor)와 Invoker (SaveButton)의 결합도가 줄어든다.
- 개방폐쇄 원칙을 준수한다. client 코드를 변경하지 않고도 새로운 Command를 추가할 수 있다.
- Redo/Undo 기능, 작업 예약 기능을 쉽게 구현할 수 있다.
- SaveCommand 외에도 PasteCommand, WriteCommand 등을 만들고,
작업을 실행할 때마다 Command Stack에 쌓아서 히스토리를 관리하면 된다. - 일부 데이터의 접근이 제한된 경우 (private) Memento 패턴으로 대체 가능하다.
- SaveCommand 외에도 PasteCommand, WriteCommand 등을 만들고,
- 간단한 Command를 조합해서 복잡한 Command를 만들어낼 수 있다.
단점
- 코드 복잡도가 높아진다.
- Reference
🍎 포스트가 도움이 되었다면, 공감🤍 / 구독🍹 / 공유🔗 / 댓글✏️ 로 응원해주세요. 감사합니다.
'프로그래밍 철학' 카테고리의 다른 글
[디자인 패턴] Prototype - 의존성 없이 객체를 복사할 때 (0) | 2023.07.16 |
---|---|
[toss] iOS 개발자를 위한 SLASH23 리뷰 (7) | 2023.06.18 |
[toss] iOS 개발자를 위한 SIMPLICITY23 리뷰 - 디자이너와 친해지기 (7) | 2023.06.04 |
[디자인 패턴] Singleton (0) | 2023.03.26 |
[디자인 패턴] Builder - 초기화 과정이 복잡할 때 (0) | 2023.02.20 |
Comments