웹개발 - Back 관련/Architecture

[ GOF ] 어댑터 패턴 - Adapter Pattern with Kotlin

정상호소인 2024. 9. 9. 20:43

Topic =  어댑터 패턴의 의도와 Kotlin 예제로 실습해보기.

 

🔑 Purpose

  • Adapter라는 중간 클래스를 통해 서로 다른 인터페이스를 가진 클래스들이 함께 작동할 수 있도록 하여 새로운 시스템을 도입하더라도 기존의 인터페이스를 변경하지 않고도 새로운 기능을 추가할 수 있도록 한다.

 

 


 

Adapter 패턴 개념

 

Adapter 패턴은 120V 돼지코 콘센트에 220V 플러그를 지닌 전자기기를 사용하기 위해 어뎁터를 사용하는 것과 같이 기존 클래스를 사용하고 싶지만, 그 인터페이스가 다른 코드와 호환되지 않을 때 사용할 수 있다.

 

즉, Client가 사용하고 있는 Target(120V 콘센트)에 호환되지 않는 Adaptee(220V 플러그)를 Target에 만족시키기 위해 Adapter를 사용하는 것처럼 Adapter 패턴의 의도는 호환성 및 확장성이다. 

 

유지 관리 측면에서도, 충분히 테스트 되어 버그가 적던 기존의 코드에 새로운 API를 추가하려고 할 때, 기존 클래스의 코드를 수정하는 방식보다 어댑터를 통해 새로운 인터페이스를 맞추어 버그가 발생하더라도 Adapter 클래스를 중점적으로 살펴보며 수정할 수 있다.

 

120V에 220V 어뎁터를 덮어씌우는 것과 같이 Wrapper 패턴이라고 부르기도 한다.

 

위임을 통한 어댑터 vs 상속을 통한 어댑터 

🔀예제 코드에 앞서, Adapter 패턴을 적용하기 위한 방법은 상속을 통한 어댑터 패턴과 위임을 통한 어댑터 패턴으로 나누어 볼 수 있다. 각각의 장단점이 있지만 대부분의 경우 위임을 통한 어댑터가 객체지향 설계 원칙에 더 적합하다.

  • 구체적인 Adaptee에 의존하기보다, 인터페이스에 의존할 수 있다 - DIP
  • 상속을 사용할 경우, Adaptee의 모든 특성을 Adapter가 그대로 가져와야 하므로 LSP를 위반할 가능성이 높다.

 

[ 예제 코드 ]

 USB-C 타입의 충전기와 USB-C to Lightning 어뎁터를 통해 라이트닝 USB 충전 타입의 휴대폰을 충전하는 예제.

 

[ Adaptee ]

class ChargerTypeC {
    fun connection() {
        println("C 타입 USB 를 통해 전류가 흐른다.")
    }
}
  • Adaptee는 Client와 Target 입장에서 호환되지 않는 인터페이스 형식으로, Adapter를 통해 Target 형식으로 변환 되어야 하는 것이다.

 

[ Target ]

interface ChargerTypeLightning {
    fun connectionLightning()
}
  • Client가 사용할 인터페이스로, Adaptee가 변환되어야 하는 인터페이스 형식을 지정해주는 것. 라이트닝 USB 타입의 핸드폰을 충전하기 위해 필요한 메서드를 결정한다.

 

[ Adapter ]

// 위임을 통한 Adapter 패턴
class TypeCToLightningAdapter(
    private val chargerTypeC: ChargerTypeC
) : ChargerTypeLightning {

    override fun connectionLightning() {
        chargerTypeC.connection()
        println("C to lightning Adapter 연결")
        println("Lightning 타입 USB 를 통해 전류가 흐른다.")
    }
}
  • Adaptee을 생성자로 위임 받아, 필요한 메서드만 Target에 형식에 맞춰 지원한다.

 

[ 구체 Client ]

class MyPhone {

    fun charged(charger: ChargerTypeLightning ) {
        charger.connectionLightning()
    }
}
  • Target의 메서드를 적용할 대상 정의.

 

[ Client 접근( 사용 ) ]

fun adapterPatternTest() {
    val myPhone = MyPhone()

    // 1. 위임을 사용한 어댑터 패턴
    val myCharger = ChargerTypeC()
    val myAdapterCharger = TypeCToLightningAdapter(myCharger)
    myPhone.charged(myAdapterCharger)

    // 2. 상속을 통한 어댑터 패턴
    val myAdapterChargerV2 = TypeCToLightningAdapterV2()
    myPhone.charged(myAdapterChargerV2)
}

 

 

 

 

 

 

 

 

 

 

 

 


 

[ A. 오늘 복습한 내용 / B. 다음에 학습할 내용 ]

A. 무분별한 상속은 LSP 위반할 수 있다.

 

B. 탬플릿 메서드 패턴

 

 

 


[Reference]

 

JAVA 언어로 배우는 디자인 패턴 입문: 쉽게 배우는 GoF의 23가지 디자인 패턴 | 유키 히로시 | 영진

▶ 3판에서 달라진 점 ㆍ예제 프로그램을 현대 Java 언어(람다식, 확장 for문, enum형 등)로 업데이트 ㆍ이해하기 쉽게 설명하되 현대의 관점에서 예제와 설명(의존성 주입, 보안 관련 설명 등) 수정

ebook-product.kyobobook.co.kr

 

 

Koltin으로 알아보는 GoF 디자인패턴(2) — 구조 패턴

어댑터 패턴은 Adaptee를 클라이언트가 요구하는 인터페이스를 지원하는 클래스(Adpater)로 래핑하여 호환되지 않는 두 유형(Target, Adaptee) 간의 링크를 제공하는 데 사용됩니다. 어댑터 패턴을 사용

medium.com