iOS/Design Pattern
Design Pattern - Adapter Pattern
HaningYa
2020. 8. 30. 18:04
728x90
Adapter Pattern
Adapter pattern은 행위 패턴으로 상호 호환이 안되는 타입을 연결해 주는 패턴이다. 4개의 부분이 있다.
- Adapter를 사용하는 객체: 새로운 프로토콜에 의존하는 객체
- 새로운 프로토콜: 사용을 위해 필요한 프로토콜
- legacy Object: 프로토콜이 만들어 지기 전에 원래 있던 객체, 수정될 수 없고 직접적으로 integrate 할 수 없는 객체
- Adapter: 프로토콜을 준수하면서 legacy object 와 상호작용 하기 위해 만들어진 객체
실물의 어댑터의 가장 좋은 예시는 아이폰이다. 3.5mm 헤드폰 단자가 없어서 어댑터를 통해 라이트닝 단자에 연결해야 한다.
두개의 요소를 알맞게 연결해 주는 것이 Adapter Pattern 의 핵심이다.
안드로이드에서는 RecyclerView 같은 종류를 만들때 맨날 Adapter 썼었는데 iOS 로 넘어오면서 delegate, datasource 프로토콜로 너무 편했다.
Adapter Pattern 예제
3rd party authentication service 가 legacy object 의 역할을 맡을 것 이다.
import UIKit
public class GoogleAuthenticator {
public func login(
email: String,
password: String,
completion: @escaping (GoogleUser?, Error?) -> Void ) {
let token = "speical-token-value"
let user = GoogleUser(email: email,
password: password,
token: token)
completion(user, nil)
}
}
public struct GoogleUser {
public var email: String
public var password: String
public var token: String
}
- 위 코드는 써드파티 클래스로 수정될 수 없다. (legacy object 역할이다)
- 실제 Google Authentication 은 더 복잡하지만 예시로 간단하게 작성했다.
- 로그인 함수는 GoogleUser 라는 string property 로 이루어진 token 을 준다.
public protocol AuthenticationService {
func login(email: String,
password: String,
success: @escaping (User, Token) -> Void,
failture: @escaping (Error?) -> Void)
}
public struct User {
public let email: String
public let password: String
}
public struct token {
public let value: String
}
- 위 authentication protocol은 새로운 프로토콜 역할을 맡는다.
- email 과 password 가 필수이고 로그인에 성공할 경우 success 를 호출해 User와 Token 를 넘긴다.
- 앱단에서는 GoogleAuthenticator를 쓰지않고 방금 만든 프로토콜을 통해 Authenticate 를 할 것이다.
- 이렇게 하면 좋은 점이 다양한 Authentication 로직, 예를 들어 Google, Facebook 과 같은 걸 하나의 protocol 을 통해 단순화 할 수 있다는 것이다.
- Google Authenticator 를 방금 만든 AuthenticationService 프로토콜을 준수하게 만든다. (이게 어댑터 패턴이다)
public class GoogleAuthenticatorAdapter : AuthenticationService {
private var authenticator = GoogleAuthenticator()
public func login(email: String,
password: String,
success: @escaping (User, Token) -> Void.
failture: @escaping (Error?) -> Void) {
authenticator.login(email: email, password: password) {
(googleUser, error) in
guard let googleUser = googleUser else {
failure(error)
return
}
let user = Usre(email: googleUser.email,
password: googleUser.password)
let token = Token(value: googleUser.token)
success(user, token)
}
}
}
- 위 GoogleAuthenticatorAdapter 가 AuthenticationService 와 GoogleAuthenticator 사이 어댑터 역할을 담당한다.
- GoogleAuthenticator 에 대한 private reference를 선언한다.
- 프로토콜에 따라 AuthenticationService 의 로그인 함수를 구현한다. 함수 내에서 GoogleUser를 얻기위해 Google 의 로그인 매소드를 호출한다.
- failure 와 success 처리한다.
- GoogleAuthenticator 를 wrapping 함으로 써 소비자가 Google API 를 직접적으로 알 필요가 없는 장점이 있다.
- 이런 식으로 하면 나중에 수정사항이 생겨도 adapter 만 수정하면 되서 편리하다.
public class LoginViewController : UIViewController {
public var authService : AuthenticationService!
var emailTF = UITextField()
var passwordTF = UITextField()
public class func instance(
with authService: AuthenticationService)
-> LoginViewController {
let vc = LoginViewController()
vc.authService = authService
return vc
}
public func login() {
guard let email = emailTF.text,
let password = passwordTF.text else{
print("Email and password are required to inputs!")
return
}
authService.login(
email: email,
password, password,
success: {user token in
print("Auth succeeded: \(user.email), \(token.value)")
},
failure: { error in
print("Auth failed with error: no error provided")
})
}
}
}
let viewController = LoginViewController.instance(
with: GoogleAuthenticatorAdapter())
viewController.emailTF.text = "userexample.com"
viewController.passwordTF.text = "password"
viewController.login()
- LoginViewController 에서 어댑터, 프로토콜, 레거시 객체를 사용해 본 코드이다.
- authService.login을 호출하는 로그인 메소드를 만든다.
- 마지막에 textfield 에 이메일 비밀번호 적고 login() 해본다.
Adapter Pattern 사용시 주의할 점
- Adapter Pattern는 새로운 프로토콜만 준수하면 된다. 이 건 결과적으로 프로토콜 뒤의 타입에 관해 (또는 미래 수정) 에 대해 신경 쓸 필요가 없다는 뜻이다. 하지만 구현이 더 복잡해지고 가독성이 떨어지며 유지보수가 힘들어 진다.
- 실제 수정에 대한 가능성이 있다면 adapter pattern을 사용하고 수정할 가능성이 없다면 adapter 를 빼고 직접적으로 연결 하는 것이 바람직하다.
Adapter Pattern 정리
- adapter pattern 은 써드파티 라이브러리처럼 수정할 수 없는 class를 작업하는데 유용하다. 프로토콜을 사용한다.
- adapter 를 사용하기 위해 legacy object를 extend 하거나 새로운 adapter class 를 만들어야 한다.
- adapter pattern은 필요로 하는 객체가 incompatible 한 경우에도 class 를 재사용 할 수 있게 해준다.
- 미래에 수정이 될만한 코드라면 Adapter Pattern을 쓰는 것이 좋다.
728x90