Adapter[Design Pattern]

SnowCat·2023년 3월 6일
0

Design Pattern

목록 보기
7/23
post-thumbnail

의도

  • 어댑터는 호환되지 않는 인터페이스를 가진 객체들이 협업할 수 있도록 하는 구조적 디자인 패턴

문제

  • XML 데이터를 받아오는 주식 서비스의 값을 사용하고 싶다. 그런데 분석 라이브러리는 JSON 형식 데이터로만 작동하게 된다.
  • XML 형식의 데이터를 받아오도록 라이브러리를 변경할 수 있으나, 데이터가 손상될 수 있고, 혹은 아예 소스코드 접근이 불가능한 경우도 발생할 수 있다.

해결책

  • 코드와 직접 작동하는 분석 라이브러리의 모든 클래스에 대한 XML->JSON 변환 어댑터를 제작
  • 어댑터 제작 이후 어탭터를 통해서만 해당 라이브러리와 통신하도록 코드를 조정
  • 어댑터는 호출을 받으면 XML을 JSON으로 변환한 후 호출을 래핑된 분석 객체의 적절한 메서드에 전달

구조

// 그림에서 클라이언트 인터페이스와 대응되며, 다른 클래스들이 클라이언트 코드와의 호환을 위해 따르는 프로토콜
class Target {
    public request(): string {
        return 'Target: The default target\'s behavior.';
    }
}

// 기존의 서비스로 현재 클라이언트에서 제대로 해석이 불가능한 상황
class Adaptee {
    public specificRequest(): string {
        return '.eetpadA eht fo roivaheb laicepS';
    }
}

// 서비스와 클라이언트 사이의 호환성을 위해 중간에 어댑터를 생성해주어야함
// Adapter는 Adaptee를 받아 클라이언트가 이해할 수 있는 데이터 형식으로 전환
class Adapter extends Target {
    private adaptee: Adaptee;

    constructor(adaptee: Adaptee) {
        super();
        this.adaptee = adaptee;
    }

    public request(): string {
        const result = this.adaptee.specificRequest().split('').reverse().join('');
        return `Adapter: (TRANSLATED) ${result}`;
    }
}

// 클라이언트는 Target 인터페이스를 따르는 모든 데이터를 올바르게 사용 할 수 있게 됨
function clientCode(target: Target) {
    console.log(target.request());
}

console.log('Client: I can work just fine with the Target objects:');
const target = new Target();
// Target: The default target's behavior.
clientCode(target);


const adaptee = new Adaptee();
console.log('Client: The Adaptee class has a weird interface. See, I don\'t understand it:');
// Adaptee: .eetpadA eht fo roivaheb laicepS
console.log(`Adaptee: ${adaptee.specificRequest()}`);

console.log('Client: But I can work with it via the Adapter:');
const adapter = new Adapter(adaptee);
// Adapter: (TRANSLATED) Special behavior of the Adaptee.
clientCode(adapter);

적용

  • 기존 클래스를 사용하고 싶지만 인터페이스가 호환되지 않을 때 사용
    직접 짠 코드와 레거시 클래스, 타사의 클래스등과의 변환기 역할을 하는 중간 레이어 클레스 제작
  • 부모 클래스에 추가할 수 없는 공통기능들이 필요한 자식 클래스들을 재사용하려는 경우에 사용
    각 자식 클래스를 확장해서 누락된 기능들을 새 자식 클래스에 넣을 수 있지만, 공통기능을 각 클래스마다 복제해야 하는 문제 발생
    어댑터 클래스에 누락된 기능을 넣고, 객체를 래핑해 필요한 기능을 동적으로 할당 가능

구현 방법

  1. 호환되지 않는 인터페이스가 있는 클래스가 2개이상 있는지 확인
  2. 클라이언트 인터페이스를 선언하고 서비스와 통신하는 방법을 기술해둠
  3. 어댑터 클래스를 생성해 클라이언트 인터페이스를 따르게 함
  4. 서비스 객체에 reference를 저장하기 위해 어댑터 클래스에 필드를 추가
  5. 클라이언트 인터페이스의 모든 메서드를 어댑터 클래스에서 하나씩 구현
    어댑터는 인터페이스, 데이터 변환 형식만 처리해야 하며, 실제 작업의 대부분은 서비스 객체가 진행해야 함
  6. 클라이언트들은 클라이언트 인터페이스를 통해 어댑터 사용

장단점

  • 프로그램의 비지니스 로직에서 인터페이스, 데이터 변환 코드를 분리해 단일 책임 원칙, 개방/폐쇄 원칙 준수
  • 다수의 새로운 인터페이스와 클래스들을 도입해야 하기에 코드의 전반적인 복잡성 증가
    서비스 클래스를 변경 하는 것이 간단할 때도 있음

출처:
https://refactoring.guru/ko/design-patterns/adapter

profile
냐아아아아아아아아앙

0개의 댓글