본문 바로가기

Software Development/디자인 패턴

[디자인 패턴의 정석] 구조패턴 - 어댑터 패턴 (Adapter)

어댑터 패턴 Class Diagram

 

 

개발자 yeah의 Developer Story - 디자인 패턴

 

오늘은 저번 포스팅에 이어서 어댑터 패턴 을 이야기해보려 합니다.


 

개발자는 자신의 코드가 재사용되도록 하기 위해 다양한 변화를 예측하며 코드를 작성합니다.

 

하지만 동작의 변화, 데이터 타입의 불일치, 매개변수의 불일치 등 다양한 차이가 발생하여 모든 상황을 예측하여 설계할 수 없습니다.

 

따라서 우리는 이전 코드를 재사용하기 위해서는 변환 작업이 필요하게 됩니다.

 

 

 

우리는 변환 작업을 위해 우리는 보정 코드를 넣게 되는데요.

 

보정동작이 여러 곳에 분포돼 있다면 많은 영역의 코드가 수정돼야 하며, 가독성이 많이 떨어지게 됩니다.

 

이러한 경우 별도의 객체를 생성하여 보정을 처리하는 것이 좋은데요!

 

 

보정만을 위해 설계된 객체를 활용하는 방법이 바로 어댑터 패턴 입니다.

 


먼저 두 수의 나눗셈의 결과 를 알려주는 Math 클래스가 있고 이를 출력하는 Main이 있습니다.

 

public class Math {

    /* 두 수의 나눗셈의 결과를 나타내는 함수 */
    public float division(float num1, float num2) {
        return num1 / num2;
    }
}

 

public class AdapterPattern {
    public static void main(String[] args) {
    
        /* Math 를 이용한 두 수의 나눗셈 결과 */
        Math math = new Math();
        float mathResult = math.division(10, 3);
        System.out.println(mathResult);
    }
}

 

해당 Math 클래스 의 division 함수는 float형 인자를 받아 float형 결과값을 리턴하게 됩니다.

 

만약 클라이언트의 요청으로 인해 기존 Math를 유지하면서 int형 인자를 받아 int형 결과값을 리턴해주는 division을 요구했다면?

 

물론 Math 클래스 내에 int형 인자와 결과값의 새로운 division을 추가하거나 오버로딩을 통한 추가가 가능합니다.

 

 

하지만 이러한 보정 코드들은 위에서 언급한 잠재적인 문제점 을 가지고 있습니다.

 


해당 요청을 어댑터 패턴 을 활용하여 해결해보도록 하겠습니다.

 

 

어댑터 패턴 의 종류로는 크게 2가지로 나누어집니다.

 

- 클래스 어댑터  ->  다중 상속 을 활용한 방법

 

- 객체 어댑터  ->  복합 객체 를 활용한 방법

 

※ 최신 언어는 대부분 다중 상속을 지원하지 않습니다.  따라서 클래스 어댑터 방법은 배제하고 설명하도록 하겠습니다!

 

 

 

 

객체 어댑터기존 Math를 활용하기 위해 객체의 의존성을 활용하여 새로운 클래스를 정의합니다.

 

 

이때 내부적으로 객체를 재구성하며, 구성을 위해 기존 객체를 복합 객체(Math)로 활용합니다.

 

해당 복합 객체의 의존성 연결은 직접 생성하셔도 되고, 외부에서 인자로 받아도 됩니다

 

 

 

그다음 요구사항이 반영된 인터페이스를 정의합니다.

 

정의한 인터페이스를 해당 클래스에 구현하여 요구사항에 맞도록 기존 Math를 활용하여 정의합니다.

 


먼저 사용자가 요구한  int형 인자/int 반환형 division()을 인터페이스로 정의합니다.

 

/* 사용자가 요구사항을 반영한 인터페이스 */
public interface Adapter {
    /* int형 인자/반환형을 가진 division */
    int division(int num1, int num2);
}

 

 

 

다음은 기존 Math를 활용하여 새로운 클래스(NewMath)를 정의하고 해당 Adapter 인터페이스를 구현합니다.

 

public class NewMath implements Adapter{
    /* 복합 객체를 통한 의존성 주입 */
    private Math math = new Math(); //방법1. 직접 생성

    /*
    private NewMath(Math math) {    //방법2. 생성자를 통한 외부에서 인자로 주입
        this.math = math;
    }
    */

    @Override
    public int division(int num1, int num2) {
        float result = math.division((float)num1, (float)num2);
        return (int) result;
    }
}

 

다음 Adapter 인터페이스 를 구현하여 division() 을 새롭게 정의합니다.

 

이때 기존 Math의 division()을 활용하여 int형 인자/ int형 반환 division() 을 구현한 모습을 볼 수 있습니다.

 

 

 

 

다음은 main()에서의 결과입니다.

 

public class AdapterPattern {
    public static void main(String[] args) {
        /* Math 를 이용한 두 수의 나눗셈 결과 */
        Math math = new Math();
        float mathResult = math.division(10, 3);
        System.out.println(mathResult);


        /* NewMath 를 이용한 두 수의 나눗셈 결과 */
        NewMath newMath = new NewMath();
        int newMathResult = newMath.division(10, 3);
        System.out.println(newMathResult);
    }
}

 

 

 

어댑터 패턴 을 적용한 NewMath 를 통해서 int형 결과값을 받는 것을 확인할 수 있습니다.

 

이로써 어댑터 패턴 을 활용하면 기존 Math 코드에는 어떠한 수정도 없어 유지보수에 용이하며,

 

코드의 재사용을 통해 중복 코드의 발생도 줄게 됩니다.

 

 

 

 

 

이렇게 오늘은 어댑터 패턴 을 이야기해봤는데요.

 

 

궁금하시거나 질문이 있으시면 댓글로 달아주세요 !!

 

 

감사합니다!