디자인 패턴이란?
디자인 패턴은 소프트웨어 개발에서 자주 발생하는 문제에 대한 해결책을 재사용 가능한 형태로 정리한 것
개발자들 간의 공통 언어를 제공하여 효율적인 의사소통을 도움
아키텍쳐와의 차이
규모와 적용 범위
아키텍처
시스템 전체의 구조와 레이아웃을 다루며,
대규모의 시스템에서 적용 아키텍처는 시스템의 주요 구성 요소와 이들 간의 관계,
데이터 흐름, 성능 최적화, 보안 정책 등 정의
디자인 패턴
보통 클래스나 객체의 작은 규모의 디자인 문제를 해결하며,
개별 컴포넌트나 모듈 내의 상세한 구조와 상호 작용을 다룸
목적
아키텍처
시스템의 기본 구조를 정의하여 시스템의 전체적인 모습을 관리,
시스템의 성능, 확장성, 유지보수성을 보장
디자인 패턴
클래스나 객체 간의 상호 작용을 구조화하여
재사용 가능한 솔루션을 제공
정의(적용) 시점
아키텍처 시스템을 설계할 때 고려,
초기 개발 단계에서 정의
디자인 패턴
클래스나 객체의 작은 규모의 구조를 설계할 때 고려,
주로 구현 단계에서 적용
Delegate Pattern
한 객체가 다른 객체의 대리자(delegate)가 되는 디자인 패턴
다른 객체의 이벤트나 데이터를 처리하는 방식으로 구현,
객체 간의 결합도를 낮추고,
유연하고 확장 가능한 코드 작성 가능
예시
MyClass 객체를 사용
↓
MyDelegate 프로토콜을 채택한 delegate 객체를 가짐
delegate 객체의 didSomething 매서드를 호출
↓
객체의 이벤트나 데이터 처리
MyDelegateClass는 MyDelegate를 구현한 클래스
↓
didSomething 메서드에서 작업 수행
myObject는 delegateObject를 대리자로 설정
↓
doSomething 매서드 호출
MyClass는 MyDelegateClass와 독립적으로 작성되었기에
확장성 높은 코드 작성 가능
해당 Delegate 프로토콜을 클래스 전용 프로토콜(class-only protocol)로 만들기 위해
AnyObject를 상속받음
↓
MyDelegate 상속받는 구현체가 값 타입이 아닌,
참조타입인 클래스만 해당 프로토콜을 채택할 수 있음을 명시적으로 나타냄
// 프로토콜 선언
protocol MyDelegate: AnyObject {
func didSomething()
}
// 객체 선언
class MyClass {
weak var delegate: MyDelegate?
func doSomething() {
// 작업 수행
delegate?.didSomething()
}
}
// 대리자 객체 선언
class MyDelegateClass: MyDelegate {
func didSomething() {
// 작업 수행
}
}
// 사용 예시
let myObject = MyClass()
let delegateObject = MyDelegateClass()
myObject.delegate = delegateObject
myObject.doSomething()
특징
다중 상속을 지원하지 않는 Swift에서 Protocol을 사용하여 다중 상속과 유사한 구현 가능
delegate는 자체적으로 이벤트를 발생시키지 않음
다른 객체에서 호출할 때 매서드를 제공, 해당 객체에서 발생한 이벤트나 데이터 처리
UITableView, UICollectionView와 상호작용하기 위해,
delegate라는 개념을 사용하는, UICollectionViewDelegate, UITableViewDelegate가 있음
Observer Pattern
하나의 객체가 변경되었을 때,
해당 객체에 의존하는 다른 객체들에게 자동으로
알림을 보내어 변경 사항을 적용하는 디자인 패턴
예시
Observer구현
Subject 클래스는 관찰 대상이 되는 객체
Observer 프로토콜은 변경 사항을 적용할 객체들이 구현해야하는 메서드들을 가짐
addObserver, removeObserver, notifyObservers 함수를 사용하여
의존 관계를 설정하고 변경 사항을 적용.
protocol Observer: AnyObject {
func update(subject: Subject)
}
class Subject {
var observers = [Observer]()
func addObserver(_ observer: Observer) {
observers.append(observer)
}
func removeObserver(_ observer: Observer) {
if let index = observers.firstIndex(where: { $0 === observer }) {
observers.remove(at: index)
}
}
func notifyObservers() {
for observer in observers {
observer.update(subject: self)
}
}
}
class ConcreteObserver: Observer {
func update(subject: Subject) {
// 변경 사항을 적용하는 코드 작성
}
}
let subject = Subject()
let observer = ConcreteObserver()
subject.addObserver(observer)
// subject가 변경되었을 때, observer에게 알림을 보냄
subject.notifyObservers()
Notification Center 사용
NotificationCenter 객체를 사용하여 MyNotification 이름의 Notification을 등록하고 발송
addObserver 를 사용하여 Notification을 등록
post 매소드를 사용하여 Notification 발송
handleNotification 에서 Notification 처리
// Notification을 등록하는 코드
NotificationCenter.default.addObserver(self, selector: #selector(handleNotification(_:)), name: Notification.Name("MyNotification"), object: nil)
// Notification을 발송하는 코드
NotificationCenter.default.post(name: Notification.Name("MyNotification"), object: nil)
// Notification을 처리하는 코드
@objc func handleNotification(_ notification: Notification) {
// Notification을 처리하는 로직을 작성
}
특징
객체 간의 결합도를 낮춰 유연성을 높임
변경사항을 적용할 객체들을 동적으로 관리할 수 있음
변경사항을 적용할 객체 간의 직접 호출을 피함
객체의 변경 사항에 대한 응답을 캡슐화하여 코드의 유지보수를 쉽게 함
주의점
통제 불가능
Observer 패턴을 사용시 한번 보내진 메시지는 모든 관찰자에게 전달, 이를 제어하거나 중단할 방법이 없음
디버깅 어려움
Observer 패턴은 메시지의 소스와 대상을 느슨하게 연결하여,
누가 이벤트를 보냈는지 또는 누가 이벤트를 수신했는지 파악하기 어려움.
순서 보장 불가
Observer 패턴을 통해 보내진 메시지는,
어떤 순서로 처리될지 보장하지 않음
메모리 누수
Observer 패턴을 사용할 때 객체가 제거될 때
관찰자 목록에서 해당 객체를 제거하는 것이 중요
밀접한 결합 Observer 패턴이나 broadcast 방법을 사용하면
이벤트 소스와 이벤트 핸들러 간에 밀접한 결합이 발생할 수 있음
Singleton Pattern
하나의 객체 인스턴스만 생성하고,
이를 전역에 제공하는 디자인 패턴
앱 전역에 공유해야하는 상태나 기능을 효율적으로 관리 가능
예시
shared 정적 상수를 통해 전역에 단 하나인 인스턴스 생성
생성자는 private로 선언하여 외부에서 인스턴스 생성 방지
doSomething() 함수를 앱 전역에서 사용 가능
// Singleton 클래스 선언
class MySingleton {
static let shared = MySingleton()
private init() {}
func doSomething() {
// 작업 수행
}
}
// 사용 예시
MySingleton.shared.doSomething()
특징
전역에서 단 하나의 인스턴스만 생성되어, 객체 간의 의존성을 줄임
앱 전역에 공유해야하는 상태나 기능(Database, Userdata 등)을 효율적으로 관리
주의점
전역 상태
'싱글톤'은 애플리케이션의 전역 상태를 관리하므로 프로그램의 예측이 어려움
싱글톤이 유지해야 하는 상태는 가능한 최소한으로 제한해서 관리
테스트하기 어려움
'싱글톤'이 전역 상태를 가지고 있으므로, 테스트하기 어려움
테스트 케이스 간에 상태가 공유되므로,
하나의 테스트 케이스가 다른 테스트 케이스에 영향을 줄 수 있음
이를 피하기 위해, 테스트할 수 있는 방법을 고려하여 싱글톤을 설계해야 함
결합도 증가
싱글톤은 코드 간의 높은 결합도를 초래할 수 있음
클래스가 직접 싱글톤 인스턴스에 의존하게 되면,
그 클래스는 싱글톤과 떼어낼 수 없는 결합을 형성하게 됨
이를 완화하기 위해, 의존성 주입과 같은 기법을 사용하여
싱글톤에 대한 의존성을 줄일 수 있음
'IOS > Swift-Study' 카테고리의 다른 글
| [Swift-Study] iOS 앱 개발 심화 1주차 - 동영상 재생하기 (0) | 2024.05.01 |
|---|---|
| [Swift-Study] iOS 앱 개발 심화 1주차 - 비동기 프로그래밍 (0) | 2024.05.01 |
| [Swift-Study] iOS 앱 개발 심화 1주차 - 아키텍처 (0) | 2024.05.01 |
| [Swift-Study] iOS 앱 개발 숙련 1주차 - 화면전환 (1) | 2024.04.14 |
| [Swift-Study] iOS 앱 개발 숙련 1주차 - 내부 저장소 (0) | 2024.04.12 |