주식 시장 모니터링 앱을 만들고 있다고 가정하자. 이 앱은 XML 형식의 여러 소스에서 주식 데이터를 다운로드한 다음 사용자에게 보기 좋은 차트와 다이어그램을 표시한다.
앱과 호환되지 않는 형식의 데이터를 예상하기 때문에 분석 라이브러리를 "있는 그대로" 사용할 수 없다.
어느 시점에서 스마트 타사 분석 라이브러리를 통합하여 앱을 개선하기로 결정했다. 하지만 문제가 발생했다. 분석 라이브러리는 오직 JSON 형식의 데이터만을 받아들여 작동하는 것이다.
XML과 함께 작동하도록 라이브러리를 변경 할 수 있다. 그러나 이것은 라이브러리에 의존하는 일부 기존 코드를 손상시킬 수 있다. 더군다나 처음부터 라이브러리의 소스 코드에 액세스 할 수는 없어 이 접근 방식은 불가능할 수 있다.
Adapter를 생성한다. 이 객체는 한 객체의 인터페이스를 다른 객체가 이해할 수 있도록 변환하는 특수 객체이다.
Adapter는 뒤에서 일어나는 변환의 복잡성을 숨기기 위해 객체 중 하나를 래핑한다. 래핑된 객체는 어댑터를 인식하지도 못한다. 예를 들어 미터 및 킬로미터 단위로 작동하는 객체를 피트/마일과 같은 영국식 단위로 변환하는 어댑터로 래핑할 수 있다.
Adapter는 데이터를 다양한 형식으로 변환할 수 있을 뿐만 아니라 다양한 인터페이스를 가진 객체가 협업하는 데 도움이 될 수 있다. 작동 방식은 다음과 같다.
때로는 양방향으로 호출을 변환하는 양방향 Adapter를 만드는 것도 가능하다.
호환되지 않는 형식이라는 딜레마를 해결하기 위해 코드가 직접 작동하는 분석 라이브러리의 모든 클래스에 대해 XML-JSON Adapter를 만들 수 있다. 그런 다음 이러한 어댑터를 통해서만 라이브러리와 통신하도록 코드를 조정한다. 어댑터가 호출을 수신하면 들어오는 XML 데이터를 JSON 구조로 변환하고 호출을 래핑된 분석 객체의 적절한 메서드에 전달한다.
이 구현은 객체 결합 원칙을 사용한다: 어댑터는 한 객체와 다른 객체를 감싼 것의 인터페이스를 구현한다. 그것은 유명한 프로그래밍 언어 전부에서 구현할 수 있다.
import XCTest
// Target은 클라이언트 코드에 의해 사용되는 구체적인 도메인 인터페이스를 정의한다.
class Target {
func request() -> String {
return "Target: The default target's behavior."
}
}
// Adaptee는 몇가지 유용한 행동을 포함한다. 그러나 이것의 인터페이스는 존재하는 클라이언트 코드와 맞지 않는다.
// Adaptee는 클라이언트 코드가 사용하기 전에 몇가지 적응이 필요하다.
class Adaptee {
public func specificRequest() -> String {
return ".eetpadA eht fo roivaheb laicepS"
}
}
// 인터페이스와 호환되도록 한다.
class Adapter: Target {
private var adaptee: Adaptee
init(_ adaptee: Adaptee) { // 의존성 주입
self.adaptee = adaptee
}
override func request() -> String {
return "Adapter: (TRANSLATED) " + adaptee.specificRequest().reversed()
}
}
// 클라이언트 코드는 Target 인터페이스를 따르는 모든 클래스를 지원한다.
class Client {
// ...
static func someClientCode(target: Target) {
print(target.request())
}
// ...
}
/// Let's see how it all works together.
class AdapterConceptual: XCTestCase {
func testAdapterConceptual() {
print("Client: I can work just fine with the Target objects:")
Client.someClientCode(target: Target())
let adaptee = Adaptee()
print("Client: The Adaptee class has a weird interface. See, I don't understand it:")
print("Adaptee: " + adaptee.specificRequest())
print("Client: But I can work with it via the Adapter:")
Client.someClientCode(target: Adapter(adaptee))
}
}
import XCTest
import UIKit
/// 어댑터 디자인 패턴
///
/// 의도: 클래스의 인터페이스를 클라이언트가 기대하는 인터페이스로 변환한다.
/// 어댑터를 사용하면 호환되지 않는 인터페이스로 인해, 작동하지 않는 클래스가 함께 작동할 수 없다.
class AdapterRealWorld: XCTestCase {
/// 예를 들어. 우리의 앱이 완벽하게 페이스북 인증하는 것과 함께 잘 동작한다고 가정하자.
/// 그러나, 사용자는 당신에게 Twitter를 통해 등록하는 것을 요구한다.
/// 불행하게도, Twitter SDK는 다른 인증 메서드를 가지고 있다.
/// 맨 처음, 새로운 프로토콜인 'AuthService' 프로토콜을 생성하고 Facebook SDK의 authorization 메서드를 삽입한다.
/// 두 번째, Twitter SDK의 익스텐션을 작성하고 단지 간단하게 리디렉트 하는것으로 AuthService 프로토콜의 메서드를 구현한다.
/// 세 번째, Facebook SDK의 익스텐션을 작성한다. 이 시점에서 Facebook SDK에 의해 이미 구현된 어떤 메서드 코드도 작성하면 안된다.
/// 단지 컴파일러에게 두 SDK가 같은 인터페이스를 가진 것을 말한다.
func testAdapterRealWorld() {
print("Starting an authorization via Facebook")
startAuthorization(with: FacebookAuthSDK())
print("Starting an authorization via Twitter.")
startAuthorization(with: TwitterAuthSDK())
}
func startAuthorization(with service: AuthService) {
/// The current top view controller of the app
let topViewController = UIViewController()
service.presentAuthFlow(from: topViewController)
}
}
protocol AuthService {
func presentAuthFlow(from viewController: UIViewController)
}
class FacebookAuthSDK {
func presentAuthFlow(from viewController: UIViewController) {
/// SDK 메서드를 호출하고 뷰컨트롤러를 넘긴다.
print("Facebook WebView has been shown.")
}
}
class TwitterAuthSDK {
func startAuthorization(with viewController: UIViewController) {
/// SDK 메서드를 호출하고 뷰컨트롤러를 넘긴다.
print("Twitter WebView has been shown. Users will be happy :)")
}
}
extension TwitterAuthSDK: AuthService {
/// 이것이 어댑터이다.
/// 우리는 또 다른 클래스를 생성할 수 없고 기존의 존재하는 것을 확장만 할 수 있다.
func presentAuthFlow(from viewController: UIViewController) {
print("The Adapter is called! Redirecting to the original method...")
self.startAuthorization(with: viewController)
}
}
extension FacebookAuthSDK: AuthService {
/// 이 익스텐션은 컴파일러에게 두 SDK가 단지 같은 인터페이스를 공유한다는 것을 말해주기 위함이다.
}
출처: refactoring.guru