관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] Mixin 패턴(mix-in), Traits 패턴 본문

Architecture (swift)

[iOS - swift] Mixin 패턴(mix-in), Traits 패턴

jake-kim 2021. 12. 17. 23:01

Mixin, Traits 패턴

  • Mixin, Traits 패턴: 특정 클래스에서 어떤 기능이 필요할 때, 이 기능을 interface로 정의하여 이 interface만 준수하면 바로 기능을 사용할 수 있도록 설계된 패턴
    • 상속이 아닌 포함
    • 코드 재사용성을 높이고 다중상속으로 인해 발생할 상속의 모호성 문제 제거
    • SingleTon 대신에 Traits 패턴을 사용하면 scope 관리가 용이 (SingleTon은 어디서든 접근 가능하지만 Traits는 해당 프로토콜을 준수한 곳에서만 사용 가능하므로)
  • 상속이 깊어질수록 복잡도가 증가하므로, Mixin 패턴을 통해 flatten the inheritance hierarchy을 위함
    (사진 출처 - https://machinethink.net/blog/mixins-and-traits-in-swift-2.0/)

Mixin or Traits을 사용하지 않은 경우 - 상속이 많으므로 복잡도 증가
Mixin or Traits을 통해 flatten 형태로 유지

  • Mixin과 Traits의 차이
    • Mixin: 인터페이스의 body에 상태를 나타내는 stored property가 존재 (swift에서는 불가능하므로 모두 Traits만 가능)
    • Traits: 인터페이스의 body에 method 구현이 존재 (swift에서 프로토콜 default implementation을 의미)

Traits 구현 방법

  • protocol의 default implementation을 통해 기능(메소드)를 정의하고, 해당 프로토콜을 준수하면 기능을 사용할 수 있는 형태
  • ex) 문자열에 띄어쓰기가 있는지 확인하는 기능 mixin
    • protocol, default implementation 정의
      // StringCheck.swift
      
      protocol StringCheck {
        func isExistSpaceCharactor(_ string: String) -> Bool
      }
      
      extension StringCheck {
        func isExistSpaceCharactor(_ string: String) -> Bool {
          let trimmedString = string.replacingOccurrences(of: " ", with: "")
          return trimmedString.count != string.count
        }
      }
  • 사용하려는 Class에서 위 protocol을 준수하여 사용
    class ViewController: UIViewController, StringCheck {
    
      override func viewDidLoad() {
        super.viewDidLoad()
        
        print(self.isExistSpaceCharactor("abc def")) // true
        print(self.isExistSpaceCharactor("abcdef")) // false
        
      }
    }​

static으로 Traits 구현

ex) UserDefaults 구현

  • 서비스 - UserDefaultServiceType 정의 
  • 외부에서 접근하는 인터페이스 - UserDefaultTraits 정의
    // UserDefaultTraits.swift
    
    // 인터페이스
    protocol UserDefaultTraits {
      static var userDefaultService: UserDefaultServiceType.Type { get }
    }
    
    extension UserDefaultTraits {
      static var userDefaultService: UserDefaultServiceType.Type { userDefaultService.self }
    }
    
    // 서비스
    protocol UserDefaultServiceType {
      static var isLogin: Bool { get set }
    }
    
    private enum UserDefaultService: UserDefaultServiceType {
      static var isLogin: Bool {
        get {
          return UserDefaults.standard.bool(forKey: "isLogin")
        }
        set {
          UserDefaults.standard.set(newValue, forKey: "isLogin")
        }
      }
    }​
  • 사용하는쪽 - UserDefaultTraits을 상속받고 타입으로 접근하여 사용
class ViewController: UIViewController, UserDefaultTraits {

  override func viewDidLoad() {
    super.viewDidLoad()
    
    Self.userDefaultService.isLogin = true
  }
}

* mix-in패턴 중 Traits를 사용한 예제는 PhotoTraits구현한 포스팅 글 참고

 

* 참고

- Mixin, Traits: https://machinethink.net/blog/mixins-and-traits-in-swift-2.0/

- mixin wiki: https://ko.wikipedia.org/wiki/%EB%AF%B9%EC%8A%A4%EC%9D%B8

Comments