어댑터 패턴(Adapter Pattern)

어댑터 패턴은 호환되지 않는 인터페이스를 가진 클래스를 클라이언트에서 사용할 수 있게 만드는 패턴으로, 

실제로 많은 경우에서 사용됩니다. 예를 들어, 다음과 같은 경우가 있습니다.

1. 외부 라이브러리를 사용하는 경우
외부 라이브러리를 사용할 때, 라이브러리가 제공하는 인터페이스가 클라이언트에서 사용해야 하는 인터페이스와 다를 수 있습니다. 

이 경우, 어댑터 패턴을 사용하여 외부 라이브러리를 클라이언트에서 쉽게 사용할 수 있게 합니다.

// 외부 라이브러리의 인터페이스
class ExternalLib {
    func doSomethingComplicated() {
        // ...
    }
}

// 클라이언트가 사용할 인터페이스
protocol MyLib {
    func doSomethingSimple()
}

// 어댑터 클래스
class MyLibAdapter: MyLib {
    private let externalLib = ExternalLib()
    
    func doSomethingSimple() {
        externalLib.doSomethingComplicated()
    }
}

// 클라이언트 코드
let myLib: MyLib = MyLibAdapter()
myLib.doSomethingSimple() // 외부 라이브러리의 복잡한 동작을 간단하게 호출합니다.

2. 새로운 버전의 인터페이스를 사용하는 경우
새로운 버전의 인터페이스를 사용하려면, 기존 코드를 수정해야 하는 경우가 있습니다. 

이 경우, 어댑터 패턴을 사용하여 기존 코드를 수정하지 않고도 새로운 버전의 인터페이스를 사용할 수 있게 합니다.

// 새로운 버전의 인터페이스
protocol NewInterface {
    func doSomethingNew()
}

// 기존 인터페이스
protocol OldInterface {
    func doSomethingOld()
}

// 어댑터 클래스
class OldToNewAdapter: NewInterface {
    private let oldInterface: OldInterface
    
    init(_ oldInterface: OldInterface) {
        self.oldInterface = oldInterface
    }
    
    func doSomethingNew() {
        oldInterface.doSomethingOld()
    }
}

// 클라이언트 코드
let oldInterface: OldInterface = /*...*/
let newInterface: NewInterface = OldToNewAdapter(oldInterface)
newInterface.doSomethingNew() // 기존 인터페이스의 동작을 새로운 버전의 인터페이스로 간단하게 호출합니다.

3. 다양한 데이터 소스를 사용하는 경우
여러 개의 데이터 소스를 사용하는 경우, 각각의 데이터 소스마다 다른 인터페이스를 가질 수 있습니다. 

이 경우, 어댑터 패턴을 사용하여 각각의 데이터 소스를 동일한 인터페이스로 간단하게 사용할 수 있습니다.

protocol DataSource {
    func getData() -> [String]
}

// 첫번째 데이터 소스
class DataSource1 {
    func retrieveData() -> [String] {
        // ...
        return ["data1", "data2", "data3"]
    }
}

// 두번째 데이터 소스
class DataSource2 {
    func retrieve() -> [String] {
        // ...
        return ["data4", "data5", "data6"]
    }
}

// 어댑터 클래스
class DataSourceAdapter: DataSource {
    private let dataSource1 = DataSource1()
    private let dataSource2 = DataSource2()
    
    func getData() -> [String] {
        return dataSource1.retrieveData() + dataSource2.retrieve()
    }
}

// 클라이언트 코드
let dataSource: DataSource = DataSourceAdapter()
let data = dataSource.getData() // 어댑터 클래스를 사용하여 두 데이터 소스의 데이터를 모두 가져옵니다.

위 예시에서는 DataSource라는 프로토콜을 정의하여, 각각의 데이터 소스를 동일한 인터페이스로 사용할 수 있게 합니다. 

DataSourceAdapter 클래스는 DataSource 프로토콜을 따르며, DataSource1과 DataSource2 클래스의 인스턴스를 가지고 있습니다. 

getData() 메서드에서는 두 데이터 소스에서 가져온 데이터를 합쳐서 반환합니다. 

클라이언트 코드에서는 DataSource 프로토콜을 사용하여 DataSourceAdapter 클래스를 호출하므로, 

데이터 소스가 변경되어도 클라이언트 코드를 수정할 필요가 없습니다.

이처럼 어댑터 패턴은 호환되지 않는 인터페이스를 가진 클래스를 클라이언트에서 사용할 수 있게 만들어줍니다. 

이를 통해 기존 코드를 수정하지 않고도 새로운 인터페이스를 사용하거나, 

여러 개의 데이터 소스를 동일한 인터페이스로 사용할 수 있게 됩니다.

 

감사합니다.