Adapter

chris·2021년 6월 5일
0

design pattern

목록 보기
5/11

Intent

Adapter는 호환되지 않는 interface가 있는 object가 공동 작얼을 할 수 있도록 하는 구조적 design pattern입니다.

Problem

주식시장 모니터링 앱을 만들고 있다고 상상해봅시다. 이 앱은 XML 형식의 여러 소스에서 주식 데이터를 다운로드 한 다음 사용자에게 멋진 차트와 다이어그램을 표시합니다.

어느 시점에서 타사 스마트 분석 라이브러리를 도입하여 개선하기로 결정하는데 문제가 있습니다. 해당 라이브러리는 JSON형식의 데이터로만 작동합니다.

XML과 함께 작동하도록 라이브러리를 변경할 수 있습니다. 그러나 이로 인해 라이브러리에 의존하는 기존 코드가 손상 될 수 있습니다. 더 나쁜 것은 처음부터 라이브러리의 소스 코드에 액세스 할 수 없어 이 접근 방식은 불가능합니다.

Solution

이 경우 adapter를 만들 수 있습니다. 이것은 다른 개체가 이해할 수 있도록 한 개체의 interface를 반환하는 특수 개체입니다.

Adapter는 개체 중 하나를 감싸서 장면 뒤에서 발생하는 변환의 복잡성을 숨 깁니다. Wrapping 된 개체는 어댑터도 인식하지 못합니다. 예를 들어 미터 및 킬로미터 단위로 작동하는 개체를 모든 데이터를 피트 및 마일과 같은 영국식 단위로 변환하는 어댑터를 사용하여 래핑 할 수 있습니다.

Adapter는 데이터를 다양한 형식으로 변환 할 수 있을뿐만 아니라 서로 다른 interface를 가진 객체가 협업하는데 도움을 줄 수 있습니다. 작동 방식은 다음과 같습니다.

  1. Adapter는 기존 개체 중 하나와 호환되는 interface를 얻습니다.
  2. 이 interface를 사용하면 기존 게체가 adapter의 method를 안전하게 호출 할 수 있습니다.
  3. 호출을 받으면 adapter는 두 번째 개체에 요청을 전달하지만 두 번째 개체가 예상하는 형식과 순서로 전달됩니다.

때로는 양방향으로 통화를 변환 할 수 있는 양방향 adapter를 만드는 것도 가능합니다.

주식 시장 앱으로 돌아 갑시다. 호환되지 않는 형식의 딜레마를 해결하기 위해 코드가 직접 작동하는 분석 라이브러리의 모든 클래스에 대해 XML-JSON adapter를 만들 수 있습니다. 그런 다음 이러한 adapter를 통해서만 라이브러리와 통신하도록 코드를 조정합니다. Adapter가 호출을 받으면 들어오는 XML 데이터를 JSON 구조로 변환하고 wrapping 된 분석 개체의 적절한 메서드에 호출을 전달합니다.

Real-World Analogy


한국에서 처음 캐나다로 여행을 가서 노트북을 충전하려고 할 때 놀라움을 느낄 수 있습니다. 전원 플러그 및 소켓 표준은 국가마다 다르기 때문에 한국용 충전기로는 충전을 할 수 없습니다. 이 경우 양국에서 모두 사용이 가능한 전원 플러그 adapter를 사용하여 문제를 해결할 수 있습니다.

Structure

Object adapter

이 구현에서는 개체 구성 원칙을 사용합니다. Adapter는 한 개체의 interface를 구현하고 다른 개체를 wrapping합니다. 널리 사용되는 모든 프로그래밍 언어로 구현할 수 있습니다.

  1. Client는 프로그램의 기존 비지니스 로직을 포함하는 클래스입니다.
  2. Client interface는 다른 클래스가 client 코드와 협업 할 수 있도록 따라야 하는 프로토콜을 설명합니다.
  3. Service는 유용한 클래스(보통 타사 또는 레거시)입니다. Interface가 호환되지 않기 때문에 client는 이 클래스를 직접 사용할 수 없습니다.
  4. Adapter는 Client와 Service 모두에서 작동 할 수 있는 클랙스입니다. 서비스 객체를 wrapping하면서 client interface를 구현합니다. Adapter는 adapter interface를 통해 client로부터 호출을 수신하고 이를 이해 할 수 있는 형식으로 wrapping된 service 개체에 대한 호출로 변환합니다.
  5. Client 코드는 client interface를 통해 adapter와 함께 작동하는 한 구체적인 Adapter 클래스에 연결되지 않습니다. 덕분에 기존 client 코드를 손상시키지 않고 새로운 유형의 adapter를 프로그램에 도입 할 수 있습니다. 이는 service 클래스의 interface가 변경되거나 교체 될 때 유용 할 수 있습니다. Client 코드를 변경하지 않고 새 adapter 클래스를 만들 수 있습니다.

Class adapter

이 구현에서는 상속을 사용합니다. Adapter는 동시에 두 개체의 interface를 상속합니다. 이 접근 방식은 C++와 같이 다중 상속을 지원하는 프로그래밍 언어로만 구현할 수 있습니다.

  1. Class adapter는 client와 service 모두에서 동작을 상속하므로 객체를 wrapping 할 필요가 없습니다. 재정의 된 method 내에서 적응이 발생합니다. 결과 adapter는 기존 client class 대산 사용할 수 있습니다.

Pseudocode

이 adapter pattern의 예는 사각 못과 둥근 구멍 사이의 고전적인 충돌을 기반으로 합니다.

Adapter는 사각형 지름의 절반에 해당하는 반지름(즉, 사각형 말뚝을 수용 할 수 있는 가장 작은 원의 반지름)을 가진 둥근 말뚝인 것처럼 가장합니다.

// Say you have two classes with compatible interfaces:
// RoundHole and RoundPeg.
class RoundHole is
    constructor RoundHole(radius) { ... }

    method getRadius() is
        // Return the radius of the hole.

    method fits(peg: RoundPeg) is
        return this.getRadius() >= peg.getRadius()

class RoundPeg is
    constructor RoundPeg(radius) { ... }

    method getRadius() is
        // Return the radius of the peg.


// But there's an incompatible class: SquarePeg.
class SquarePeg is
    constructor SquarePeg(width) { ... }

    method getWidth() is
        // Return the square peg width.


// An adapter class lets you fit square pegs into round holes.
// It extends the RoundPeg class to let the adapter objects act
// as round pegs.
class SquarePegAdapter extends RoundPeg is
    // In reality, the adapter contains an instance of the
    // SquarePeg class.
    private field peg: SquarePeg

    constructor SquarePegAdapter(peg: SquarePeg) is
        this.peg = peg

    method getRadius() is
        // The adapter pretends that it's a round peg with a
        // radius that could fit the square peg that the adapter
        // actually wraps.
        return peg.getWidth() * Math.sqrt(2) / 2


// Somewhere in client code.
hole = new RoundHole(5)
rpeg = new RoundPeg(5)
hole.fits(rpeg) // true

small_sqpeg = new SquarePeg(5)
large_sqpeg = new SquarePeg(10)
hole.fits(small_sqpeg) // this won't compile (incompatible types)

small_sqpeg_adapter = new SquarePegAdapter(small_sqpeg)
large_sqpeg_adapter = new SquarePegAdapter(large_sqpeg)
hole.fits(small_sqpeg_adapter) // true
hole.fits(large_sqpeg_adapter) // false

Applicability

기존 class를 사용하려고 하지만 해당 interface가 나머지 코드와 호환되지 않는 경우 adapter class를 사용합니다.

Adapter pattern을 사용하면 코드와 레거시 class, 타사 class 또는 이상한 interface가 있는 다른 class 사이에서 변환기 역할을 하는 중간 계층 class를 만들 수 있습니다.

Superclass에 추가 할 수 없는 일부 공통 기능이 없는 기존의 여러 subclass를 재사용하려는 경우 pattern을 사용합니다.

각 subclass를 확장하고 누락 된 기능을 새 subclass에 넣을 수 있습니다. 그러나 이러한 모든 새 class에서 복제해야 하므로 냄새가 정말 안 좋습니다.

훨씬 더 우아한 해결책은 누락 된 기능을 adapter class에 넣는 것입니다. 그런 다음 adapter 내부에 누락 된 기능이 있는 개체를 wrapping하여 필요한 기능을 동적으로 얻습니다. 이 작업을 수행하려면 대상 class에 공통 interface가 있어야 하며 adapter 필드는 해당 interface를 따라야합니다. 이 접근 방식은 Decorator pattern과 매우 유사합니다.

How to Implement

  1. Interface가 호환되지 않는 class가 두개 이상 있는지 확인하십시오.
  • 변경 할 수 없는 유용한 서비스 class(종종 타사, 레거시 또는 많은 기존 종속성 포함)
  • Service class 사용으로 이점을 얻을 수 있는 하나 또는 여러 client class.
  1. Client interface를 선언하고 client service와 통신하는 방법을 설명합니다.

  2. Adapter class를 만들고 client interface를 따르도록 합니다. 지금은 모든 method를 비워 둡니다.

  3. Service class에 대한 참조를 저장하려면 adapter class에 필드를 추가합니다. 일반적인 방법은 생성자를 통해 이 필드를 초기화하는 것이지만 method 호출할 때 adapter에 전달하는 것이 더 편리한 경우도 있습니다.

  4. 하나씩 adapter class에서 client interface의 모든 method를 구현합니다. Adapter는 interface 또는 데이터 형식 변환만 처리하여 대부분의 실제 작업을 서비스 개체에 위임해야 합니다.

  5. Client는 client interface를 통해 adapter를 사용해야 합니다. 이렇게 하면 client 코드에 영향을 주지 않고 adapter를 변경하거나 확장 할 수 있습니다.

Pros and Cons

O 단일 책임 원칙. 프로그램의 기본 비즈니스 로직에서 interface 또는 데이터 변환 코드를 분리 할 수 있습니다.

O 개방/폐쇄 원칙. Client interface를 통해 adapter와 함께 작동하는 한 기존 client 코드를 손상시키지 않고 새로운 유형의 adapter를 프로그램에 도입 할 수 있습니다.

X 새로운 interface 및 class 세트를 도입해야 하므로 코드의 전반적인 복잡성이 증가합니다. 때로는 나머지 코드와 일치하도록 service class를 변경하는 것이 더 간단합니다.

Relations with Other Patterns

  • Bridge는 일반적으로 미리 설계되어 응용 프로그램의 일부를 서로 독립적으로 개발할 수 있습니다. 반면에 Adapter는 일반적으로 기존 앱과 함께 사용되어 호환되지 않는 일부 클래스가 잘 작동하도록합니다.

  • Adapter는 기존 객체의 인터페이스를 변경하는 반면 Decorator는 인터페이스를 변경하지 않고 객체를 향상시킵니다. 또한 Decorator는 Adapter를 사용할 때는 불가능한 재귀 적 구성을 지원합니다.

  • Adapter는 래핑 된 객체에 다른 인터페이스를 제공하고 Proxy는 동일한 인터페이스를 제공하며 Decorator는 향상된 인터페이스를 제공합니다.

  • Facade는 기존 개체에 대한 새 인터페이스를 정의하는 반면 Adapter는 기존 인터페이스를 사용 가능하게 만들려고합니다. 어댑터는 일반적으로 하나의 개체 만 래핑하는 반면 Facade는 개체의 전체 하위 시스템과 함께 작동합니다.

  • Bridge, State, Strategy (및 어느 정도 어댑터)는 매우 유사한 구조를 가지고 있습니다. 실제로 이러한 모든 패턴은 작업을 다른 개체에 위임하는 구성을 기반으로합니다. 그러나 그들은 모두 다른 문제를 해결합니다. 패턴은 특정 방식으로 코드를 구조화하기위한 단순한 방법이 아닙니다. 또한 패턴이 해결하는 문제를 다른 개발자에게 전달할 수 있습니다.

profile
software engineer

0개의 댓글