iOS 디자인 패턴 (Delegate, Observer, Singleton Pattern)

이경은·2024년 1월 24일
0

디자인 패턴의 개념에 대해 이해하고, Delegate, Observer, Singleton Pattern에 대해 학습합니다.


디자인 패턴이란?

디자인 패턴은 소프트웨어 개발에서 자주 발생하는 문제에 대한 해결책을 재사용 가능한 형태로 정리한 것입니다.

개발자들 간의 공통언어를 제공하여 효율적인 의사소통을 돕습니다.



아키텍처와의 차이

규모와 적용범위

차이점
아키텍처시스템 전체의 구조와 레이아웃을 다루며, 대규모의 시스템에서 적용
아키텍처는 시스템의 주요 구성요소와 이들 간의 관계, 데이터 흐름, 성능 최적화, 보안 정책 등 정의
디자인 패턴보통 클래스나 객체의 작은 규모의 디자인 문제를 해결.
개별 컴포넌트나 모듈 내의 상세한 구조와 상호작용을 다룸

목적

차이점
아키텍처시스템의 기본 구조를 정의하여 시스템의 전체적인 모습을 관리하고,
시스템의 성능, 확장성, 유지보수성을 보장
디자인 패턴클래스나 객체 간의 상호작용을 구조화하여 재사용가능한 솔루션을 제공

정의(적용) 시점

차이점
아키텍처시스템을 설계할 때 고려되며, 초기 개발 단계에서 정의됩니다.
디자인 패턴클래스나 객체의 작은 규모의 구조를 설계할 때 고려되며, 주로 구현 단계에서 적용됩니다.




Delegate Pattern

한 객체가 다른 객체의 대리자(delegate)가 되는 디자인 패턴입니다.

다른 객체의 이벤트나 데이터를 처리하는 방식으로 구현되며, 객체 간의 결합도를 낮추고, 유연하고 확장 가능한 코드 작성이 가능합니다.

예시

  • MyClass 객체를 사용하고 MyDelegate 프로토콜을 채택한 delegate 객체를 가짐
  • delegate 객체의 didSomething 메서드를 호출하여 객체의 이벤트나 데이터를 처리
  • MyDelegateClassMyDelegate를 구현한 클래스
  • didSomething 메서드에서 작업 수행
  • myObjectdelegateObject를 대리자로 설정하여 doSomething 메서드를 호출
  • MyClassMyDelegateClass와 독립적으로 작성되었기에 확장성 높은 코드작성 가능
  • 해당 Delegate 프로토콜을 클래스 전용 프로토콜(class-only protocol)로 만들기 위해 AnyObject를 상속받음 → MyDelegate를 상속받는 구현체가 값 타입이 아닌, 참조타입인 클래스만 해당 프로토콜을 채택할 수 있음을 명시적으로 나타냄
// 프로토콜 선언
// 클래스 전용 프로토콜로 만들기 위해 AnyObject를 상속
// MyDelegate를 상속받는 구현체가 값타입이 아닌 참조타입인 '클래스'만 MyDelegate을 채택할 수 있다는 것을 명시적으로 나타냄
protocol MyDelegate: AnyObject {
		func didSomething()
}

// MyDelegate 프로토콜을 채택한 delegate 객체를 가지는 MyClass 객체 선언
class MyClass {
		weak var delegate: MyDelegate?
		
		// doSomething 메서드를 통해 delegate 객체의 didSomething 메서드를 호출해 객체의 이벤트나 데이터를 처리
		func doSomething() {
				//작업 수행
				delegate?.didSomething()
		}
}

// MyDelegate를 구현한 대리자(delegate) 객체 선언
// didSomething 메서드에서 어떤 일을 할 것인지 구성
class MyDelegateClass: MyDelegate {
		func didSomething() {
				//작업 수행
		}
}

// 사용 예시
// myobject는 delegateObject를 대리자로 설정해 doSomething 메서드를 호출
// MyClass는 myDelegate 클래스와 독립적으로 작성되어 있기에 확장성 높은 코드작성이 가능함
// 결국 객체에 이벤트나 데이터를 처리하기에 객체 간 결합도를 낮추고 확장성을 높일 수 있는 방식으로 구현
let myObject = MyClass()
let delegateObject = MyDelegateClass()

myObject.delegate = delegateObject
myObject.doSomething()

특징

  • 다중 상속을 지원하지 않는 Swift에서 Protocol을 사용하여 다중상속과 유사한 구현 가능
  • delegate는 자체적으로 이벤트를 발생시키지 않음
  • 다른 객체에서 호출할 때 메서드를 제공하고, 해당 객체에서 발생한 이벤트나 데이터 처리
  • UITableView, UICollectionView와 상호작용하기 위해, delegate라는 개념을 사용하는 UICollectionViewDelegate, UITableViewDelegate가 있음.



Observer Pattern

하나의 객체가 변경되었을 때, 해당 객체에 의존하는 다른 객체들에게 자동으로 알림을 보내어 변경사항을 적용하는 디자인 패턴입니다.

예시

  1. 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 notifyObservsers() {
				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.notifyObservsers()
  1. 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 정적(static) 상수를 통해 전역에 단 하나인 인스턴스 생성
  • 생성자는 private로 선언하여 외부에서 인스턴스 생성 방지
  • doSomething() 함수를 앱 전역에서 사용가능
// Singleton 클래스 선언
class MySingleton {
		static let shared = MySingleton()

		private func doSomething() {
				// 작업 수행
		}
}

// 사용예시
MySingleton.shered.doSomething()

특징

  • 전역에서 단 하나의 인스턴스만 생성되어, 객체 간의 의존성을 줄임
  • 앱 전역에 공유해야하는 상태나 기능(Database, Userdata)을 효율적으로 관리

주의점

  • 전역 상태
    싱글톤은 애플리케이션의 전역상태를 관리하므로 프로그램의 예측이 어려울 수 있음
    싱글톤이 유지해야하는 상태는 가능한 최소한으로 제한해서 관리
  • 테스트하기 어려움
    싱글톤이 전역상태를 가지고 있으므로, 테스트하기 어려움
    테스트 게이스 간에 상태가 공유되므로, 하나의 테스트 케이스가 다른 테스트 케이스에 영향을 줄 수 있음
    이를 피하기 위해, 테스트할 수 있는 방법을 고려하여 싱글톤을 설계해야 함
  • 결합도 증가
    싱글톤은 코드 간의 높은 결합도를 초래할 수 있음
    클래스가 직접 싱글톤 인스턴스에 의존하게 되면, 그 클래스는 싱글톤과 떼어낼 수 없는 결합을 형성하게 됨
    이를 완화하기 위해, 의존성 주입과 같은 기법을 사용하여 싱글톤에 대한 의존성을 줄일 수 있음


Reference

Using Delegates to Customize Object Behavior | Apple Developer Documentation
Using Key-Value Observing in Swift | Apple Developer Documentation
Managing a Shared Resource Using a Singleton | Apple Developer Documentation

0개의 댓글