객체 지향 프로그래밍/디자인 패턴

디자인 패턴 이해하기

ghtis1798 2021. 3. 7. 11:55

디자인패턴 - 어댑터패턴

1. 디자인 패턴 정의

 

이전 포스팅에서 객체지향특성에 대해 알아보았고, 해당 특성을 이용하며 지켜야할 5가지 원칙이 있었습니다.

그리고 객체 지향 특성들을 '도구', 객체 지향 원칙들을 '도구 사용 방법' 이라고 비유했었습니다. 👌

디자인 패턴은 '도구의 활용 방법'이라고 할 수 있습니다.

우리는 도구의 특징과 어떤 원리로 사용해야 하는지 알고 있습니다.

칼을 예로 들면, 칼은 날카롭고 뾰족한 특징이 있습니다.

그래서 칼은 무엇인가를 자르거나, 찌르는 방식으로 사용할 수 있죠.

칼은 수술을 하거나, 요리를 하거나, 동물을 사냥하는데 활용할 수 있습니다. ✨

물론 칼로 망치질을 한다던가, 땅을 판다고해도 잡혀가지는 않습니다.

하지만 적절한 활용법은 존재합니다.

개발자들이 공통으로 겪는 '문제들에 대한 해결책'을 정리해 놓은 것이 디자인 패턴입니다. 😆

디자인 패턴을 구현하는 데는 3가지 기법이 들어갑니다.

바로 '상속'과 '인터페이스', 그리고 '객체 합성(Composition)'입니다.

객체 합성은 클래스를 상속받는 대신,

클래스의 객체를 생성해 자신의 클래스에 포함시키는 것입니다.

상속받지는 않았지만 그 클래스의 객체를 통해 메서드를 호출할 수 있게 되는 것이죠. 😊

객체 합성은 뒤에 나올 어댑터 패턴(Adapter Pattern)을 구현하는데 사용됩니다.

 

2. 어댑터 패턴의 정의와 예시

 

어댑터는 무엇인가를 변환해주는 역할을 합니다.

어댑터 예시로 전압을 바꿔주는 변압기가 있습니다.

일본 여행을 갔다오신 분들은 가정에 공급되는 전압이 한국과 다르다는 것을 아실겁니다.

한국은 220V를 사용하지만 일본은 110V를 사용하죠. 🤦‍♂️

따라서 여행 전에 일명 '돼지코'라는 변압기를 꼭 챙겨가야 하죠.

객체 지향에서 어댑터 패턴는 '객체 합성'을 통해 이런 역할을 수행합니다.

프로그램의 관점에서 어댑터 패턴의 예시를 들어볼까요? 👏👏

예시1

강아지 인형에는 '멍멍', 고양이 인형에는 '야옹'이 녹음되어 있습니다.

인형을 부르면 '멍멍', '야옹'하고 응답하는 프로그램을 만들고 싶습니다.

package exadapterPattern;

// Cat 클래스
public class Cat {
    void cry_cat() {
        System.out.println("야옹");
    }
}
package exadapterPattern;

// Dog 클래스
public class Dog {
    void cry_dog() {
        System.out.println("멍멍");
    }
}
package exadapterPattern;

public class People {
    public static void main(String[] args) {
        Cat cat = new Cat();
        Dog dog = new Dog();

        cat.cry_cat();
        dog.cry_dog();
    }
}

응답한다는 기능은 같은데 메소드명이 다릅니다.

Cat과 Dog는 각각 자기 자신을 cry_cat(), cry_dog() 하는 줄 알지만,

사람은 둘 모두를 call()로 기능을 통일해서 부르고 싶습니다.

이 때 어댑터 패턴을 사용합니다.

package exadapterPattern;

// 어댑터 클래스가 Cat 클래스의 객체를 포함(합성)하고 있습니다.
public class AdapterCat {
    Cat cat = new Cat();
    void call() {
        cat.cry_cat();
    }
}
package exadapterPattern;

// 어댑터 클래스가 Dog 클래스의 객체를 포함(합성)하고 있습니다.
public class AdapterDog {
    Dog dog = new Dog();
    void call() {
        dog.cry_dog();
    }
}
package exadapterPattern;

public class People {
    public static void main(String[] args) {
        // 어댑터 생성
        AdapterCat a_cat = new AdapterCat();
        AdapterDog a_dog = new AdapterDog();

        a_cat.call();
        a_dog.call();
    }
}

어댑터가 메소드를 사용하는 사람 입장에서 기능에 알맞게 맞춰준 것입니다.

그리고 이 과정에서 각각의 어댑터 클래스 안에 Cat와 Dog 클래스의 객체가 포함되어 있었습니다.

Cat과 Dog의 객체는 각각 자신의 메소드를 호출했고요.

어댑터 패턴은 메소드를 호출하는 쪽에서 원하는 기능에 대응하도록 변환기를 사용하는 패턴입니다.

예시2

낚시꾼이 붕어와 베스를 잡으려면 각각 다른 미끼를 사용해야 합니다.

package exadapterPattern;

// 붕어 미끼 클래스
public class Bungeo_bait {
    // 붕어를 잡는 메서드
    void get_Bungeo() {
        System.out.println("붕어를 잡았습니다.");
    }
}
package exadapterPattern;

// Beth 미끼 클래스
public class Beth_bait {
    // 베스를 잡는 메서드
    void get_Beth() {
        System.out.println("베스를 잡았습니다.");
    }
}

따라서 낚시꾼은 붕어,베스 클래스의 객체 생성 후, get_Bungeo()와 get_Beth()를 호출해야 합니다.

package exadapterPattern;

public class Fisher {
    public static void main(String[] args) {
        Bungeo_bait bungeo = new Bungeo_bait();
        Beth_bait beth = new Beth_bait();

        bungeo.get_Bungeo();
        beth.get_Beth();
    }
}

낚시꾼 입장에서 낚시하는 행위는 같지만 붕어와 베스를 잡는 메소드이름은 다릅니다.

이것을 어댑터 패턴을 이용해 기존 메서드를 변경하지 않고 get()으로 통일해주도록 하겠습니다.

package exadapterPattern;

// 베스 미끼 어댑터 -> get()으로 통일
public class AdapterBeth {
    Beth_bait beth_bait = new Beth_bait();

    void get() {
        beth_bait.get_Beth();
    }
}
package exadapterPattern;

// 붕어 미끼 어댑터 -> get()으로 통일
public class AdapterBungeo {
    Bungeo_bait bungeo_bait = new Bungeo_bait();

    void get() {
        bungeo_bait.get_Bungeo();
    }
}

이렇게 어댑터를 구성해주면 사용자인 낚시꾼 입장에서는 어떤 물고기던 get()을 사용하면 잡을 수 있게 됩니다.

package exadapterPattern;

public class Fisher {
    public static void main(String[] args) {
        AdapterBungeo bungeo = new AdapterBungeo();
        AdapterBeth beth = new AdapterBeth();

        // 어댑터를 통해 get() 메서드를 호출하면 각자의 get_bungeo(), get_beth() 호출
        bungeo.get();
        beth.get();    
    }
}

3. 정리 및 느낀점

 

어댑터 패턴을 공부하면서 처음 들었던 생각은 이렇습니다.

'상속, 인터페이스를 사용해서 변경하면 안될까?' 🤔

'오버 라이딩해서 사용하거나 메소드명만 바꿔주면 안될까?' 하는 생각이었습니다.

그럼 '어댑터 패턴'을 굳이 써야하나? 라는 생각도 들었고요. 🙄

그런데 곰곰히 생각해보니 오픈 소스 세상에서는 그렇지 않을 것 같더군요.

다른 사람이 만들어 놓은 좋은 코드들이 많이 공개되어 있습니다.

그 코드들을 가져와서 매 번 수행하는 것보다는,

어댑터 패턴을 이용해서 원하는 기능에 맞춰주는 것이 훨씬 쉬운 방법일 것 같더군요. 😃

이미 만들어져 있는 좋은 코드를 수정하기 보다는 '이용하자'는 깨달음을 얻었습니다.

이상으로 디자인 패턴 중 하나인 어댑터 패턴에 대해 다루어보았습니다. 🙌

패턴에는 이 외에도 종류가 많이 있기 때문에 이후에도 하나씩 정리해보겠습니다. 😝

 

참고자료

yaboong.github.io/design-pattern/2018/10/15/adapter-pattern/