Adapter 패턴 이란?

adapter는 adapt(개조)시키는 것이라는 의미를 가지고 있다.

어댑터는 일상생활 속에서도 흔히 접할 수 있다. 노트북을 새로 구입했을 때 구성품으로 들어있는 충전기를 보면 보통 어댑터가 달려있다. 이 어댑터는 전원 공급 장치의 전압을 변환하는 역할을 한다.

장치에 알맞지 않은 가정용 전류를 그대로 사용하면 망가질 수 있다. 어댑터는 공급되는 전류를 충전에 필요한 적절한 전류로 변환한다.

 

프로그램에서도 이미 제공되는 것을 그대로 사용할 수 없을 때 필요한 형태로 교환하고 사용해야 하는 경우가 있다. 이미 제공되는 것과 필요한 것 사이의 차이를 없애주는 패턴이 바로 Adapter 패턴이다. 즉, Adapter는 일종의 교환 장치와 같은 역할을 한다. 클래스의 인터페이스를 변환해서 호환성이 없는 인터페이스 때문에 함께 동작할 수 없는 클래스들이 함께 작동하도록 해준다. Adapter 패턴은 무언가를 감싼다는 의미로 Wrapper 패턴이라고도 불린다.

 

Adapter 패턴의 종류

  • 클래스에 의한 Adapter 패턴 (상속)
  • 인스턴스에 의한 Adapter 패턴 (위임)

 

예제 설명 - Markdown to HTML

특정 마크다운 문법으로 작성된 문자열을 HTML로 바꿔서 표시하는 예제를 만들어 보려고 한다.

# 문자열<h1>문자열</h1>
**강조할 문자열**<b>강조할 문자열</b>

이러한 형식으로 변환할 것이다.

이미 제공되는 것, Adapter, 필요로 하는 것의 관계 

 

예제 구성요소 설명

파일명 형태 역할 전원의 비유
Markdown class 제공되고 있는 것 100V
MarkdownConverter class 교환장치 어댑터
HTML interface 필요한 것 12V

 

예제(1) - 상속을 이용하는 Adapter 패턴

  1. Markdown

    public class Markdown {
     private String string;
    
     public Markdown(String string) {
         this.string = string;
     }
    
     public void showTitle() {
         if (string.startsWith("# ")) {
             this.string = string.replace("# ", "<h1>") + "</h1>";
         }
    
         System.out.println(string);
     }
    
     public void showBold() {
         if (string.startsWith("**") && string.endsWith("**")) {
             this.string = "<b>" + string.replace("**", "") + "</b>";
         }
    
         System.out.println(string);
     }
    }
  2. HTML

    public interface HTML {
    
     public abstract void showHead();
    
     public abstract void showStrong();
    }
  3. MarkdownConverter

    public class MarkdownConverter extends Markdown implements HTML {
    
     public MarkdownConverter(String string) {
         super(string);
     }
    
     @Override
     public void showHead() {
         showTitle();
     }
    
     @Override
     public void showStrong() {
         showBold();
     }
    }

    인터페이스를 활용해 다중 상속의 효과를 낸다. HTML 인터페이스를 구현해서 showHeadshowString 메서드를 구현하였다.

  1. Browser

    public class Browser {
    
     public static void main(String[] args) {
         HTML head = new MarkdownConverter("# 제목");
         HTML content = new MarkdownConverter("**안녕**");
    
         head.showHead();
         content.showStrong();
     }
    }

    Browser 클래스는 어댑터 역할의 MarkdownConverter를 사용해 마크다운 문자열을 변환해서 표시한다.
    이때 MarkdownConverter 인스턴스를 부모 타입인 HTML 인터페이스에 대입해야 한다는 점을 유의하자.
    기존에 주어지는 것의 일종인 Markdown 클래스나 Adapter가 어떻게 실현되고 있는지 main에서는 알 필요가 없다. 이로인해 MarkdownConverter를 삭제하거나, HTML의 다른 구현 클래스로 MarkdownConverter를 교체하게 되더라도 할당하는 코드만 수정하면 될 뿐 다른 부분을 수정할 필요가 없기에 유지보수가 용이해진다.

 

예제(2) - 위임을 이용하는 Adapter 패턴

예제(1)에서는 상속을 사용했다. 두번째 예제에서는 상속 대신 위임을 통해 동일한 기능을 구현해보려고 한다.
Browser 클래스와 Markdown 클래스의 구현은 예제(1)과 동일하다.
위임을 처리하기 위해 HTML은 인터페이스에서 클래스로 변경한다.

  1. HTML

    public abstract class HTML {
    
     public abstract void showHead();
    
     public abstract void showStrong();
    }
  2. MarkdownConverter
    Java에서는 다중 상속을 할 수 없기 때문에 Markdown 객체를 필드에 포함하도록 변경하였다.
    (1)에서는 부모 클래스에게 상속받은 메서드를 사용했지만, 이번에는 markdown을 매개로 showTitleshowBold를 호출한다.
    MarkdownConverter의 메서드가 호출되었을 때 자신이 직접 처리하는 것이 아니라 별도의 인스턴스인 Markdown의 메서드에 처리를 위임한다. 이렇듯 메소드의 실제 처리를 다른 인스턴스의 메소드에게 대신 맡기는 것을 위임이라고 한다.

    public class MarkdownConverter extends HTML {
      private Markdown markdown;
    
      public MarkdownConverter(String markdown) {
          this.markdown = new Markdown(markdown);
      }
    
      public void showHead() {
          markdown.showTitle();
      }
    
      public void showStrong() {
          markdown.showBold();
      }
    }

 

Adapter 패턴의 구성요소

  • Target : 필요한 메소드를 결정한다. 예제에서는 HTML이 이 역할을 한다.
  • Client : Target 역할의 메소드를 사용한다. 예제에서 Browser가 이 역할을 한다.
  • Adaptee : 개조당하는 쪽을 의미한다. 예제에서는 Markdown이 이 역할을 한다.
  • Adapter : Target 역할을 만족시킬 수 있도록 Adaptee를 개조한다. 예제에서는 MarkdownConverter가 이 역할을 한다.

좌 - 상속 / 우 - 위임을 사용했을 때의 클래스 다이어그램

 

사용했을 때의 장점

  • Adapter 패턴은 기존의 클래스를 개조해 필요한 클래스를 만든다. 이로인해 기존 클래스를 수정하지 않고 필요한 클래스를 만들 수 있다 (소스코드 재사용 가능)
  • 프로그램 검사가 쉬워진다. 위의 예제를 예로 들면, 기존에 주어지는 Markdown 클래스에는 문제가 없으므로 Adapter 역할의 클래스를 중점적으로 조사하면 된다.
  • 제공되는 클래스에 소스 코드가 없어도 메소드의 프로토타입만 알면 adapter 패턴을 적용할 수 있다
  • 여러 종류의 어댑터를 만들고, 교체가 가능하다. 예를들어 x가 5V를 담을 변수일 때, 
Volt x = new 220to5Adapter
x.use5V()
x = new 110to5Adapter
x.use5V()

이런식으로 여러 타입의 전기가 들어오더라도 어댑터를 교체하기만 하면 동일하게 동작한다.

 

 

참고자료

 

'if (study) > 디자인 패턴' 카테고리의 다른 글

[디자인 패턴] Iterator 패턴 (반복자 패턴)  (0) 2019.09.25