Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |
Tags
- SWIFT
- ios
- combine
- 스위프트
- uitableview
- HIG
- Clean Code
- rxswift
- uiscrollview
- 애니메이션
- map
- collectionview
- 리펙터링
- Refactoring
- Protocol
- RxCocoa
- swift documentation
- Human interface guide
- swiftUI
- 클린 코드
- UICollectionView
- ribs
- 리펙토링
- Observable
- clean architecture
- UITextView
- tableView
- 리팩토링
- Xcode
- MVVM
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - Swift] 5. 디자인 패턴 (구조 패턴) - 어댑터 패턴 (Adapter, Wrapper) 본문
Design Pattern (디자인 패턴)
[iOS - Swift] 5. 디자인 패턴 (구조 패턴) - 어댑터 패턴 (Adapter, Wrapper)
jake-kim 2023. 1. 5. 23:11어댑터 패턴 (Adapter, Wrapper)
- 현재 A 기능을 사용중일때 이와 유사한 B, C 기능이 계속 생겨나면서, B, C 인터페이스도 A와 동일하게 만드는 것을 목적으로 A의 프로토콜을 준수하는 BAdapter, CAdapter로 만들어서 사용하는쪽에서 코드의 변경을 최소화 하는 방법
- 가장 대표적인 예는, AuthService를 만들어 놓았을때, 네이버 로그인 및 카카오 로그인이 생겨나면서 이 것들의 인터페이스를 기존의 AuthService와 동일하게 하는 Adapter구현체를 만드는 방법
왜 Adapter 패턴을 사용하는가?
- 서비스를 사용하는 코드 레이어에서 최소한의 변경사항으로 확장성 있는 기능을 추가하기 위함
- 코드의 중복을 막을 수 있는 방법
Adapter 패턴의 예시
- 현재 AuthService 기능이 존재하는 상태
- AuthServiceType을 준수하고 있으며, 사용하는 쪽에서는 AuthServiceType을 바라보며 login(email:password:completion:) 메소드 사용
- 사용하는 쪽에서 원하는 데이터는 AuthServiceType인 상태
enum AuthError: Error {
}
struct User {
let id: String
let name: String
let age: Int
}
protocol AuthServiceType {
func login(email: String, password: String, completion: (Result<(User), AuthError>) -> ())
}
final class AuthService: AuthServiceType {
func login(email: String, password: String, completion: (Result<(User), AuthError>) -> ()) {
// login 시도
completion(.success(()))
}
}
- 사용하는 쪽 - AuthServiceType을 준수하는 authService 기능 존재
class ViewController: UIViewController {
// 에시 편의상 해당 코드에서 인스턴스 생성 (정석은 해당 VC 생성하는 쪽에서 로 인스턴스 주입해주기)
let authService: AuthServiceType = AuthService()
override func viewDidLoad() {
super.viewDidLoad()
authService.login(email: "email", password: "password") {
print($0)
}
}
}
- 사용하는 쪽에서는 로그인하는데 필요한 정보는 AuthServiceType에 나와있으며, email과 password 뿐이라고만 인식하는 상태라서 AuthServiceType만을 고려
- 이때 네이버로그인, 카카오로그인이 생겨나게 되면?
- 네이버, 카카오 모두 다른 User 모델을 사용하고 parameter로 naverKey, kakaoKey를 넘겨야 하는 상황
struct NaverUser {
let naverID: String
let name: String
let email: String
}
class NaverAuthService {
func login(email: String, password: String, naverKey: String, completion: (Result<(NaverUser), AuthError>) -> ()) {
}
}
struct KakaoUser {
let kakaoID: String
let name: String
let email: String
}
class KakaoAuthService {
func login(email: String, password: String, kakaoKey: String, completion: (Result<(KakaoUser), AuthError>) -> ()) {
}
}
- 만약 Adapter없이 바로 사용하면 사용하는쪽에서는 NaverUser 키, KakaoUser 키 각각 넣어주어야 하는 상태이며, 둘 다 로그인하는 공통적인 목적이 있지만 코드가 나위어진 상태
- 기존 코드에서 바라 보고 있던 인터페이스인 AuthService를 준수하는 구현체 NavetAuthAdapter와 KakaoAuthAdapter를 만들어서 사용
Adapter를 사용하여 개선하기
- Adapter를 사용하지 않은 경우
- 사용하는 쪽에서 authService만 따르다가 새로운 naver, kako auth service가 생겨나면서 새로운 값(naverKey, kakaoKey, KakaoUser, NaverUser)에 신경써야 하는 점이 존재
class ViewController: UIViewController {
let authService: AuthServiceType = AuthService()
// new
let naverAuthService = NaverAuthService()
let kakaoAuthService = KakaoAuthService()
override func viewDidLoad() {
super.viewDidLoad()
authService.login(email: "email", password: "password") {
print($0)
}
naverAuthService.login(email: "email", password: "password", naverKey: "naverKey") {
print($0)
}
kakaoAuthService.login(email: "email", password: "password", kakaoKey: "kakaoKey") {
print($0)
}
}
}
- NaverAuthService, KakaoAuthService 모두 사용하는 쪽에서 관심을 가지던 AuthServiceType을 준수하는 Adapter 구현체를 만들어서 사용
class NaverAuthAdapter: AuthServiceType {
let naverAuthService = NaverAuthService()
func login(email: String, password: String, completion: (Result<(User), AuthError>) -> ()) {
naverAuthService.login(email: "", password: "", naverKey: "naverKey") { result in
completion(.success(.init(id: "", name: "", age: 1)))
}
}
}
class KakaoAuthAdapter: AuthServiceType {
let kakaoAuthService = KakaoAuthService()
func login(email: String, password: String, completion: (Result<(User), AuthError>) -> ()) {
kakaoAuthService.login(email: "", password: "", kakaoKey: "kakaoKey") { result in
completion(.success(.init(id: "", name: "", age: 1)))
}
}
}
- 사용하는 쪽
- 기존 AuthServiceType 그대로 login 요청 메소드 시그니쳐도 같고, user 모델도 기존에 쓰던 모델 그대로 사용하면 되므로 사용하는쪽에서 코드 변경이 최소화 되었으므로 어뎁터 패턴의 목표 달성
class ViewController2: UIViewController {
let authService: AuthServiceType = AuthService()
// new
let naverAuthAdapter: AuthServiceType = NaverAuthAdapter()
let kakaoAuthAdapter: AuthServiceType = KakaoAuthAdapter()
override func viewDidLoad() {
super.viewDidLoad()
authService.login(email: "email", password: "password") {
print($0)
}
naverAuthAdapter.login(email: "email", password: "password") {
print($0)
}
kakaoAuthAdapter.login(email: "email", password: "password") {
print($0)
}
}
}
* 전체 코드: https://github.com/JK0369/ExAdapter
* 참고
https://www.kodeco.com/books/design-patterns-by-tutorials/v3.0/chapters/12-adapter-pattern
https://refactoring.guru/ko/design-patterns/adapter
'Design Pattern (디자인 패턴)' 카테고리의 다른 글
[iOS - Swift] 4. 디자인 패턴 (구조 패턴) - 브릿지 패턴 (Bridge) (0) | 2023.01.01 |
---|---|
[iOS - Swift] 2. 디자인 패턴 (생성 패턴) - 빌더 (Builder) (0) | 2022.12.28 |
[iOS - Swift] 1. 디자인 패턴 (생성 패턴) - 추상 팩토리 (Abstract Factory) (0) | 2022.12.27 |
Comments