Design Pattern

어댑터 패턴 Adapter Pattern

La.place 2019. 5. 26. 12:56

 두세 번 정리하고, 더 이상 쓰지 않을 것 같았던 JavaScript로 디자인 패턴 정리하기 그 네 번째. 이번 주인공은 Adapter Pattern(어댑터 패턴) 이다. 어댑터 패턴이란 무엇인가? 코드를 보기 전에 간단한 예시를 보도록 하자. 한국에서는 220V를 쓰는데, 일본은 110V를 사용한다. 그렇다면 220V모양으로 만들어진 전자제품을 일본에서 쓰려면 어떻게 해야 할까? 110V로 변환해주는 무언가를 사용해주면 된다(돼지코라 불리는 변압기/어댑터를 사용한다). 간단하다. 중간에서 한번 변환해주면 되는 것이다. 이 간단한 생각을 코드에 녹여보도록 하자.

 

 여기에 글자를 출력하는 프린터 클래스가 있다고 하자. 프린터는 pushText(text); 메서드를 통해 출력할 글자들을 저장하고, print() 메서드를 이용하여 입력받은 글자들을 공백으로 이어 붙여 출력한다.

// Printer.js

class Printer {
    constructor() {
        this.textArr = [];
    }

    pushText(text) {
        this.textArr.push(text);
    }

    print() {
        return this.textArr.join(' ');
    }
}

 자 이제 프린터를 만들었으니 직접 사용해볼 시간이다. 프린터에 "Hello", "Design", "Pattern" 세 글자를 입력하고 출력하는 메인 코드를 작성해보자.

// main.js

import Printer from './Printer'; 

let printer = new Printer();
printer.pushText('Hello');
printer.pushText('Design');
printer.pushText('Pattern');

let result = printer.print();
console.log(result); // Hello Design Pattern

 여기까지는 문제가 없다. 그러나 문제는 주로 변화에서 발생하며, 현실 세계는 끝임없이 환경이 변화한다. 만약 우리가 이 코드를 3개월간 사용하다 해시태그(#)를 붙여서 출력하는 프린터가 출시하여, 우리는 그 프린터로 교체를 해야한다고 가정해보자. 설상가상으로 우리가 받은 새로운 프린터의 명세(Interface)는 현재와 미묘하게 다르다고 한다.

// HashTagPrinter.js

class HashTagPrinter {
    constructor() {
        this.textArr = [];
    }

    pushText(text) {
        this.textArr.push(text);
    }

    printWithHashTag() {  // print -> printWithHashTag로 변경
        return this.textArr.map(text => `#${text}`).join(' ');
    }
}

이제 메인 함수의 코드를 새로운 프린터로 교체해 보도록 하자.

// main.js

import HashTagPrinter from './HashTagPrinter';

let printer = new HashTagPrinter();  // HashTagPrinter printer로 교체
printer.pushText('Hello');
printer.pushText('Design');
printer.pushText('Pattern');

let result = printer.print(); // printer는 명세가 맞지않아 에러가 발생한다.
console.log(result);

 이전 포스팅 중에서 좋은 코드는 최소한의 변경으로 변화에 대응할 수 있다고 언급한 적이 있다. 일단 새로운 HashTagPrinter 객체를 생성해서 printer에 할당하는 부분은 필수로 변경해 줘야 한다. 그러나 그 아래의 실질적인 비즈니스 로직에서는 불필요한 변경은 최소화하고 싶다. 위 코드에서는 마치 220V로 만든 가전기기를 110V에서 사용할 수 없듯이, HashTagPrinter에는 print()가 아닌 printWithHashTag()를 사용하고 있어서 컴파일 에러가 발생한다. 그렇다면 방법은 두 가지다. let result = printer.printWithHashTag()로 변경해 주던가, 아니면 변압기 역할을 하는 어떤 무언가를 만들어 주던가. 첫번째 방법이 쉬어 보일 수 있으나, 실제 코드에서는 저 부분만 수정한다는 보장이 없을뿐더러, 만약 우리가 다시 이전 프린터를 사용한다고 하면 또다시 같은 곳을 수정해줘야 한다. 따라서 두 번째 설루션인 돼지코(Adapter)를 만들어 보도록 하자.

 

 어댑터(Adapter)를 만드는 방법은 간단하다. 명세가 맞지 않는 부분을 맞춰주기만 하면 되는 것으로, 위 프린터 예제에서는 printWithHashTag()가 printer()를 호출했을 때 동작하도록 만들어 주면 된다.

// HashTagAdapter.js

class HashTagAdapter {
    constructor(hashTagPrinter) {
        this.printer = hashTagPrinter;
    }

    pushText(text) {
        this.printer.pushText(text);
    }

    print() {
        return this.printer.printWithHash(); // 220V -> 110V 변환!
    }
}

완성된 풀 코드는 다음과 같다.

// main.js

import HashTagPrinter from './HashTagPrinter';
import HashTagAdapter from './HashTagAdapter';

class HashTagPrinter {
    constructor() {
        this.textArr = [];
    }

    pushText(text) {
        this.textArr.push(text);
    }

    printWithHash() {
        return this.textArr.map(text => `#${text}`).join(' ');
    }
}

class HashTagPrinterAdapter {
    constructor(hashTagPrinter) {
        this.printer = hashTagPrinter;
    }

    pushText(text) {
        this.printer.pushText(text);
    }

    print() {
        return this.printer.printWithHash();
    }
}

let printer = new HashTagAdapter(new HashTagPrinter());
printer.pushText('Hello');
printer.pushText('Design');
printer.pushText('Pattern');

console.log(printer.print()); // #Hello #Design #Pattern

 어댑터 패턴은 한번 익혀두면, 웬만한 인터페이스 호환성 문제는 쉽게 해결할 수 있을 것이다. 또 상속을 이용하면 더 복잡한 로직에서 중복을 줄일 수 있으니 이부분은 연구해 보길 바란다.