본문 바로가기
디자인패턴

디자인패턴 - 어댑터 패턴

by KBS 2024. 2. 18.
728x90

어댑터 패턴

  • 구조적 패턴 카테고리
  • 서로 호환성이 없는 인터페이스 / 클래스 들을 연결시켜 동작 할 수 있게 도와준다.

적용 사례

  • 외부 라이브러리 (Auth, Payment, Media)
    • 외부 라이브러리를 사용중인데 현제 시스템상 맞지않아서 업데이트가 필요할때, 외부 라이브러리 클래스나 인터페이스에 직접적으로접근 못하는 상황일때 어댑터를 사용하여 중간에 작업을 변환한다
  • 레거시 시스템 부분 확장
    • 기존 시스템을 부분적으로 확장해야하는데 인터페이스나 클래스가 앱 전역에서 사용되는 비중이 클때 비용문제를 생각하여 어댑터 클래스를 부분적으로 만들어주고 호환 안되는 부분만 어댑터 클래스를 사용하여 변경한다

구성 요소

  • 클라이언트
  • 인터페이스
  • 서비스/외부요소
  • 어댑터

코드

interface ISmartDevice{
    connect(): boolean;
    device: string;
}

export default ISmartDevice
class TV implements ISmartDevice {
    private _device: string;

    constructor(){
        this._device = 'TV';
    }

    connect(){
        if(this.device){
            console.log(`connecting.. ${this.device}`)
            return true
        }
        return false
    }

    get device(){
        return this._device;
    }
}

export default TV
class Speaker implements ISmartDevice {
    private _device: string;

    constructor(){
        this._device = 'Speaker';
    }

    connect(){
        if(this.device){
            console.log(`connecting.. ${this.device}`)
            return true
        }
        return false
    }

    get device(){
        return this._device;
    }
}

export default Speaker
class SmartHomeClient {
    addConnection(device: ISmartDevice){
        try{
            device.connet();
            console.log(`연결 완료 : ${device.device}`)
        } catch {
            console.log(`에러 : ${device.constructor.name} 연결에 실패하였습니다.`)
        }
    }
}

const smartHomeClient = new SmartHomeClient();

smartHomeClient.addConnection(new TV());
smartHomeClient.addConnection(new Speaker());

기존의 TV클래스와 Speaker클래스는 인터페이스가 맞게 작성되었기에 잘 동작하게 된다. 하지만 추가적으로 Monitor클래스를 기존 시스템에 추가하려고한다. 해당 클래스는 다음과 같다

interface IMonitor {
    makeConnection(): string;
    getDeviceName(): string;
}


class Monitor implements IMonitor{
    private _device: string;

    constructor(){
        this._device = 'Monitor';
    }

    makeConnection(){
        console.log(`연결중.. ${this.getDeviceName()}`)
    }

    getDeviceName(){
        return this._device;
    }
}

export default Monitor

연결 하려고하는 메서드 이름도 다르고 구현 방식도 다르게 되어있다. 그래서 현 상황에서는 호환이 되지않아 오류가 발생한다.
이럴때 어탭터 클래스를 사용하여 변환하게 해준다 어댑터 클래스는 아래와 같다

class MonitorAdaptor implements ISmartDevice{
    private _target;

    constructor(target: Monitor){
        // 모니터 클래스를 전달받아서 참조한다.
        this._target = target
    }

    connect(){
        // 변환 작업
        this._target.makeConnection();
        return true
    }

    get device(){
        // 변환 작업
        return this._target.getDeviceName();
    }
}

맞지않는 클래스를 받아서 인터페이스에 맞게 구현해준다. 하지만 실제 동작은 Monitor객체의 동작을 하게 된다. 이제 다시 클라이언트 코드로 가면

class SmartHomeClient {
    addConnection(device: ISmartDevice){
        try{
            device.connet();
            console.log(`연결 완료 : ${device.device}`)
        } catch {
            console.log(`에러 : ${device.constructor.name} 연결에 실패하였습니다.`)
        }
    }
}

const smartHomeClient = new SmartHomeClient();

smartHomeClient.addConnection(new TV());
smartHomeClient.addConnection(new Speaker());
// 모니터클래스를 직접 넣는게 아니라 모니터 어댑터 클래스를 넣어준다.
smartHomeClient.addConnection(new MonitorAdaptor(new Monitor));

어댑터 패턴을 활용하면 기존 클라이언트 로직이나 인터페이스를 크게 변경하지 않고 별도의 심플한 어댑터 클래스를만들어서 외부 서비스가 기존 서비스에 이미 존재하는 것 처럼 보이게 하고 최소한의 의존성으로 잘 동작하게 만들어줍니다.

728x90

댓글