1. 어댑터 패턴

어댑터 디자인 패턴은 기존 클래스의 인터페이스를 다른 인터페이스로 사용할 수 있도록 하는 구조 패턴이다. 호환되지 않는 두 인터페이스를 연결하여 함께 작동하도록 하는 다리 역할을 한다. 이 패턴에는 독립적이거나 호환되지 않는 인터페이스의 기능을 결합하는 역할을 하는 Adapter 라는 단일 클래스가 포함된다. 이름은 어댑터(Adapter) 외에도 래퍼(Wrapper) 로 명명되기도 한다.

 

2. 어댑터 패턴 구성 요소

출처 : geeksforgeeks - adapter pattern

  1. Target interface
    • 설명 : 클라이언트가 기대하는 인터페이스를 정의한다. 클라이언트 코드가 사용할 수 있는 연산 집합을 나타낸다.
    • 역할 : 클라이언트 코드가 상호 작용하는 공통 인터페이스 이다.
  2. Adaptee
    • 설명: 새 시스템에 통합해야 하는 호환되지 않는 인터페이스가 있는 기존 클래스 또는 시스템이다.
    • 역할: 인터페이스 불일치로 인해 클라이언트 코드가 직접 사용할 수 없는 클래스 또는 시스템이다.
  3. Adapter
    • 설명: 대상 인터페이스를 구현하고 내부적으로 어댑터의 인스턴스를 사용하여 대상 인터페이스와 호환되도록 하는 클래스이다.
    • 역할: 어댑터의 인터페이스를 대상 인터페이스와 일치하도록 조정하는 브리지 역할을 한다.
  4. Client
    • 설명: 대상 인터페이스를 사용하여 객체와 상호 작용하는 코드입니다. adaptee 와 adapter 의 구체적인 구현 세부 사항은 알지 못합니다.
    • 역할: adapter 를 통해 시스템에 adaptee 를 통합함으로써 이점을 얻는 코드이다.

 

3. 어댑터 패턴이 필요한 경우

  1. 기존 코드 통합
    • 시나리오: 새 코드나 시스템에서 기대하는 인터페이스와 호환되지 않는 인터페이스를 가진 기존 코드나 컴포넌트가 있는 경우.
    • 필요: 어댑터 패턴을 사용하면 원래 코드를 수정하지 않고도 기존 컴포넌트를 새 시스템에 원활하게 통합할 수 있다.
  2. 기존 기능 재사용
    • 시나리오 : 중요한 기능을 제공하지만 원하는 인터페이스에 맞지 않는 클래스나 컴포넌트를 재사용하려는 경우.
    • 필요 : 어댑터 패턴을 사용하면 새 코드에서 기대하는 인터페이스와 호환되는 어댑터를 만들어 기존 코드를 재사용할 수 있다.
  3. 상호 운용성
    • 시나리오: 서로 다른 시스템이나 구성 요소가 함께 작동하도록 해야 할 때, 특히 인터페이스가 서로 다른 경우.
    • 필요성: 어댑터 패턴은 호환되지 않는 인터페이스를 가진 시스템이 효과적으로 협업할 수 있도록 다리 역할을 한다.

 

4. 어댑터 패턴은 지양하는 경우

  1. 인터페이스가 안정적인 경우
    • 시나리오: 기존 시스템과 새 시스템의 인터페이스가 안정적이고 자주 변경될 것으로 예상되지 않는 경우.
    • 이유: 어댑터는 진화하거나 호환되지 않는 인터페이스를 다룰 때 가장 유용하다.
  2. 직접 수정이 가능한 경우
    • 시나리오: 기존 시스템의 소스 코드를 제어할 수 있고 대상 인터페이스와 일치하도록 인터페이스를 직접 수정할 수 있는 경우.
    • 이유: 기존 코드를 수정할 수 있다면 어댑터를 도입하는 것보다 인터페이스를 직접 적용하는 것이 더 간단하고 간단한 해결책이다.
  3. 여러 개의 어댑터가 필요한 경우
    • 시나리오: 시스템에 다양한 구성 요소를 위한 수많은 어댑터가 필요하고 이러한 어댑터를 관리하는 것이 너무 복잡해지는 경우.
    • 이유: 많은 수의 어댑터를 관리하면 복잡성이 증가하고 유지 관리 문제가 발생할 수 있습니다.
  4. 어댑터가 모호성을 유발하는 경우
    • 시나리오: 어댑터를 도입하여 전체 시스템 아키텍처에 모호함이나 혼란을 초래하는 경우
    • 이유: 어댑터의 존재로 인해 시스템 설계가 명확하지 않거나 이해하기 어려울 수 있다.

 

5. 어댑터 패턴의 장점

  1. 장점
    • 기존 코드를 변경하지 않고 원하는 인터페이스 구현체를 만들어 재사용할 수 있다. (OCP 충족)
    • 기존 코드가 하던 일과 특정 인터페이스 구현체로 변환하는 작업을 각기 다른 클래스로 분리하여 관리할 수 있다.
  2. 단점
    • 새 클래스가 생겨 복잡도가 증가할 수 있다. 경우에 따라서는 기존 코드가 해당 인터페이스를 구현하도록 수정하는 것이 좋은 선택일 수 있다.

 

6. 의사코드 & 구현 코드

출처 : refactoring.guru - adapter pattern

 

sample code (출처 : refactoring.guru)

  • 둥근 구멍들에 정사각형 못들을 맞추기 가능한지 확인하기
public class Demo {
    public static void main(String[] args) {
        // client
        RoundHole hole = new RoundHole(5);
        
        // target
        RoundPeg rpeg = new RoundPeg(5);

        if (hole.fits(rpeg)) {
            System.out.println("Round peg r5 fits round hole r5.");
        }

        // adaptee
        SquarePeg smallSqPeg = new SquarePeg(2);
        SquarePeg largeSqPeg = new SquarePeg(20);

        // adapter
        SquarePegAdapter smallSqPegAdapter = new SquarePegAdapter(smallSqPeg);
        SquarePegAdapter largeSqPegAdapter = new SquarePegAdapter(largeSqPeg);

        if (hole.fits(smallSqPegAdapter)) {
            System.out.println("Square peg w2 fits round hole r5.");
        }
        if (!hole.fits(largeSqPegAdapter)) {
            System.out.println("Square peg w20 does not fit into round hole r5.");
        }
    }
}

 

 

7. 어댑터 패턴 예시

  1. java
    1. Arrays.asList, Collections.list
    2. java.io package : InputStreamReader, BufferedStreamReader
  2. spring
    1. HandlerAdapter