L-j-h-c / TIL

CS, Swift, Java, C++, 개발 관련 공부한 내용 정리
12 stars 0 forks source link

[Design Pattern] Adapter Pattern #39

Closed L-j-h-c closed 2 years ago

L-j-h-c commented 2 years ago

Adapter Pattern이란?

어댑터 패턴은 기존에 사용하던 Service와 새로운 Service를 같은 목적으로 사용할 수 있도록 하는 디자인 패턴이다. 새로운 Service에 동일한 인터페이스를 채택하여 기존의 Service와 호환될 수 있도록 만들어 준다.

Target : 기존에 사용하던 Service가 따르던 프로토콜 & 새로운 Service를 함께 사용하려면 따라야 하는 Protocol

protocol Sharing {
    func post(message: String) -> Bool
}

Client : 기존에 존재하던 Service들

class FBSharer: Sharing {
    func post(message: String) -> Bool {
        print("Message \(message) shared on Facebook")
        return true
    }
}

class TwitterSharer: Sharing {
    func post(message: String) -> Bool {
        print("Message \(message) shared on Twitter")
        return true
    }
}

Adaptee : 새로이 도입한 서비스, 기본적인 구현을 변경할 수 없다.

// Third-party class - we cannot modify its implementation
public class RedditPoster {
    public func share(text: String,
                      completionHandler: ((Error?) -> Void)?) {
        print("Message \(text) posted to Reddit")
        completionHandler?(nil)
    }
}

새롭게 도입한 서비스가 특정 사유로 인해 변경할 수 없거나, 기능을 유지하고 싶은 경우가 있다. 기존의 사용하던 서비스에 통합시키기 위해, 아래와 같이 Sharing이라는 Target 인터페이스를 extension을 통해 구현해준다.

extension RedditPoster: Sharing {
    func post(message: String) -> Bool {
        self.share(text: message, completionHandler: nil)
        return true
    }
}

결과물 : 기존의 서비스와 새로운 서비스를 통합하여 만든 서비스

// Sharer utility
// A naive approach to integrate the incompatible RedditPoster type
public class Sharer {
    private let services: [Platform: Sharing] = [.facebook: FBSharer(),
                                                 .twitter: TwitterSharer(),
                                                 .reddit: RedditPoster()]

    private lazy var redditPoster = RedditPoster()

    public func share(message: String,
                      serviceType: Platform) {
        guard let service = services[serviceType] else {
            return
        }
        service.post(message: message)
    }

    public func shareEverywhere(message: String) {
        for service in services.values {
            service.post(message: message)
        }
    }
}

위의 Sharer 클래스에 존재하는 services들은 Sharing이라는 Target 인터페이스를 공유한다. 따라서 forEach 문에서 service.post()와 같은 식의 메서드 호출이 가능하다.

테스트

// Testing
let sharer = Sharer()
sharer.shareEverywhere(message: "First post!")
L-j-h-c commented 2 years ago

참고문헌

핑구님 블로그 - 기존 로그인과 소셜 로그인의 결합

L-j-h-c commented 2 years ago

느낀점

위의 예제에서는 extension을 이용하여 단순히 Target에 대해서 새롭게 구현해주고 있다. 이러한 방식도 유의미하지만, 핑구님의 블로그에 있는 예시처럼 새로운 service가 가진 메서드를 한번 Wrapping하여 사용하는 것이 많은 부분에 사용될 수 있는 좋은 아이디어인 것 같다.