inblog logo
|
parangdajavous
    Design pattern

    Design pattern 3. Adapter Pattern

    김미숙's avatar
    김미숙
    Jul 22, 2025
    Design pattern 3. Adapter Pattern
    Contents
    ✅ Adapter Pattern실습1실습2실습3예제✅ 객체 어댑터 (Object Adapter) vs 클래스 어댑터 (Class Adapter)✅ 어댑터 패턴 vs 전략 패턴 비교

    ✅ Adapter Pattern

    • 서로 다른 인터페이스를 가진 클래스들끼리 함께 동작할 수 있도록 중간에 어댑터 클래스를 두어 변환(연결)시켜주는 디자인 패턴
    • 인터페이스가 맞지 않는 클래스들을 서로 연결해주는 구조 패턴
    • 호환되지 않는 두 개의 클래스를 중간에서 연결해주는 중재자역할

    🔧 언제 쓰나요?

    • 기존 코드는 그대로 두고,
    • 새로운 코드가 기존 인터페이스와 맞지 않을 때
    • 레거시 시스템 재사용이 필요한 경우

    🎯 목적

    호환되지 않는 인터페이스들을 맞춰서 기존 코드에 수정 없이 새 기능을 끼워넣을 수 있게 한다.

    🧩 구성 요소

    구성 요소
    설명
    Target (목표 인터페이스)
    클라이언트가 기대하는 인터페이스
    Adaptee (적용 대상)
    기존에 있는 클래스 (호환되지 않음)
    Adapter (어댑터)
    Target을 구현하고, 내부적으로 Adaptee를 호출하여 연결

    💡 구조 그림

    Client | V Target (인터페이스) ↑ Adapter (중간 변환기) ↓ Adaptee (호환 안 되는 기존 클래스)

    ✅ 예시 코드 (Java)

    기존 클래스 (Adaptee)
    class OldCharger { public void chargeWithOldCable() { System.out.println("구형 케이블로 충전 중..."); } }
    클라이언트가 원하는 인터페이스 (Target)
    interface NewCharger { void chargeWithUSB(); }
    어댑터 클래스
    class ChargerAdapter implements NewCharger { private OldCharger oldCharger; public ChargerAdapter(OldCharger oldCharger) { this.oldCharger = oldCharger; } @Override public void chargeWithUSB() { // 내부적으로 구형 메서드 호출 oldCharger.chargeWithOldCable(); } }
    사용 예
    public class Main { public static void main(String[] args) { OldCharger oldCharger = new OldCharger(); NewCharger adapter = new ChargerAdapter(oldCharger); adapter.chargeWithUSB(); // 출력: 구형 케이블로 충전 중... } }

    📌 장점

    • 기존 코드 수정 없이 새로운 인터페이스에 맞춰 사용 가능
    • 레거시 시스템 재사용 가능
    • SOLID의 **OCP(개방-폐쇄 원칙)**에 부합

    ❗️ 단점

    • 클래스 수 증가 (복잡도 약간 증가)
    • 다중 어댑터 조합 시 구조가 복잡해질 수 있음

    🔄 예시로 기억하기 좋은 사례

    실제 사례
    설명
    콘센트 변환기
    한국 플러그 → 유럽 플러그 변환
    ListAdapter in Android
    데이터 → RecyclerView UI 변환
    InputStreamReader in Java
    바이트 스트림(InputStream) → 문자 스트림(Reader)
     

    실습1

    OuterTiger (외부 요소)
    package ex03; // 외부 요소 - 내가 만들지 않아서 손 댈 수 없음! public class OuterTiger { private String fullName = "호랑이"; public String getFullName() { return fullName; } }
    TigerAdapter
    package ex03; /* * 어뎁터를 만들땐 클래스명만 봐도 어뎁터인 것을 알 수 있게 해야함 * */ public class TigerAdapter extends Animal { private OuterTiger outerTiger; // 의존성 주입 public TigerAdapter(OuterTiger outerTiger) { this.outerTiger = outerTiger; } // 동일한 함수명으로 끼워 맞춘다 public String getName() { return outerTiger.getFullName(); } }
    App
    package ex03; public class App { public static void main(String[] args) { Mouse m1 = new Mouse(); Doorman d1 = new Doorman(); d1.쫓아내(m1); Cat c1 = new Cat(); d1.쫓아내(c1); TigerAdapter t1 = new TigerAdapter(new OuterTiger()); d1.쫓아내(t1); } }

    실습2

    UserProfile
    package ex99; // 추상화 public class UserProfile { public int getId() { return 0; } public String getUsername() { return "정원"; } }
    Authentication
    package ex99; // 추상화 public abstract class Authentication { public abstract UserProfile login(); }
    NaverProfile
    package ex99; public class NaverProfile extends UserProfile { private int id; private String mainName; public NaverProfile(int id, String mainName) { this.id = id; this.mainName = mainName; } @Override public int getId() { return id; } public String getUsername() { return mainName; } }
    NaverAuthentication
    package ex99; public class NaverAuthentication extends Authentication { @Override public UserProfile login() { System.out.println("네이버 로그인 완료"); return new NaverProfileAdapter(new NaverProfile(2, "haha")); } }
    App
    package ex99; import java.util.Scanner; // FacebookProfile -> uId, uName (이게 추가될때 기존 코드를 손되지 않을 수 있게 하는게 목표) public class App { static void mainPage(UserProfile profile) { System.out.println("로그인 하신 아이디는 " + profile.getUsername() + " 입니다."); } public static void main(String[] args) { Scanner sc = new Scanner(System.in); String provider = sc.nextLine(); // 객체 생성 전담 클래스를 만들어서 처리해주면 좋다. Authentication authentication; if (provider.equals("google")) { authentication = new GoogleAuthentication(); } else if (provider.equals("kakao")) { authentication = new KakaoAuthentication(); } else if (provider.equals("facebook")) { authentication = new FacebookAuthentication(); } else if (provider.equals("naver")) { authentication = new NaverAuthentication(); } else { System.out.println("지원하지 않는 Provider 입니다."); return; } UserProfile userProfile = authentication.login(); mainPage(userProfile); } }
    notion image

    실습3

    Emai
    package ex98; public interface Email { int send(String to, String from, String msg); }
    EmailAdapter
    • Mock 객체이므로 이름을 EmailMock 라고 짓기도 함
    package ex98; // Mock 객체 - 가짜 public class EmailAdapter implements Email { public int send(String to, String from, String msg) { System.out.println("진짜 이메일 전송됨"); return 1; } }
    OuterEmail
    package ex98; public class OuterEmail implements Email { public int send(String to, String from, String msg) { System.out.println("진짜 이메일 전송됨"); return 1; } }
    App
    package ex98; public class App { static int start(Email email) { return email.send("받는놈", "보내는놈", "메시지"); } public static void main(String[] args) { Mouse m1 = new Mouse(); Doorman d1 = new Doorman(); Cat c1 = new Cat(); // 동물이 침입하려고 했어요!! (email 보내고 쫓아내야함) int result = start(new OuterEmail()); if (result == 1) { d1.쫓아내(m1); d1.쫓아내(c1); } } }

    예제

    package ex97; // 인터페이스는 내부의 모든 메서드를 구현해야한다 interface 나이프 { void 킬(); void 요리(); } // adapter // interface 내부의 메서드를 걸러내는 용도로도 만들 수 있다 // 구현할 메서드를 제외하고 override 하면, 해당 adapter를 상속했을 때 구현할 메서드만 재정의하면 됨 abstract class 전투칼 implements 나이프 { @Override public void 요리() { } } abstract class 요리칼 implements 나이프 { @Override public void 킬() { } } // 덱스는 킬 메서드, 백종원은 요리 메서드만 구현가능 class 덱스 extends 전투칼 { @Override public void 킬() { } } class 백종원 extends 요리칼 { @Override public void 요리() { } } public class App { public static void main(String[] args) { } }
     

    ✅ 객체 어댑터 (Object Adapter) vs 클래스 어댑터 (Class Adapter)

    항목
    객체 어댑터 (Object Adapter)
    클래스 어댑터 (Class Adapter)
    구현 방식
    어댑터가 Adaptee를 필드로 포함(Composition, 포함 관계)
    어댑터가 Adaptee를 상속(Inheritance, extends)
    유연성
    ✔ 더 유연함 (여러 클래스 조합 가능)
    ❌ 덜 유연함 (단일 상속만 가능)
    자바 지원
    자바에서 권장됨 (다중 상속 불가이므로)
    자바에서는 제한적 (extends는 하나만 가능)
    예시 코드
    new ChargerAdapter(new OldCharger())
    class ChargerAdapter extends OldCharger
    다형성 활용
    O
    X

    1. 객체 어댑터 (Object Adapter)

    🔍 개념
    어댑터 클래스가 Adaptee(기존 클래스)를 포함(Composition)하여 클라이언트가 기대하는 인터페이스를 구현하는 방식
     
    🔧 구성
    • Adapter는 Target 인터페이스를 구현
    • 내부에 Adaptee 객체를 필드로 가지고 있음
    • 메서드 호출 시, 내부의 Adaptee 인스턴스를 사용하여 위임
     
    📌 예시 구조
    class Adapter implements Target { private Adaptee adaptee; public Adapter(Adaptee adaptee) { this.adaptee = adaptee; } @Override public void request() { adaptee.specificRequest(); // 변환 로직 } }
     
    ✅ 장점
    • 자바에서 선호됨 (다중 상속 불가)
    • Adaptee의 서브클래스도 자유롭게 사용 가능
    • 더 유연하고 재사용성 높음
     

    2. 클래스 어댑터 (Class Adapter)

    🔍 개념
    어댑터 클래스가 Adaptee를 상속(Inheritance)하고, 동시에 Target 인터페이스도 구현하는 방식
     
    🔧 구성
    • Adapter 클래스는 Adaptee를 extends하고 Target을 implements
    • 메서드 호출은 상속받은 메서드를 직접 사용하거나 오버라이드함
     
    📌 예시 구조
    class Adapter extends Adaptee implements Target { @Override public void request() { specificRequest(); // Adaptee 메서드 사용 } }
    ⚠️ 제약
    • 자바는 다중 상속을 지원하지 않기 때문에
      • 이미 다른 클래스를 상속 중이라면 이 방식 사용 불가
         
    🔄 두 방식 비교 요약
    항목
    객체 어댑터
    클래스 어댑터
    구현 방법
    포함 (Composition)
    상속 (Inheritance)
    유연성
    ✔ 높음 (여러 조합 가능)
    ❌ 낮음 (상속 한 번만 가능)
    자바에서 추천
    ✅ 선호됨
    제한적 사용
    구조적 복잡도
    조금 복잡
    구조 단순
    재사용성
    높음
    낮음
    다형성
    지원 (필드 교체 가능)
    제한적
     

    ✅ 어댑터 패턴 vs 전략 패턴 비교

    항목
    Adapter Pattern
    Strategy Pattern
    목적
    기존 코드의 인터페이스를 변경하여 호환성 확보
    실행 중에 행위를 바꿀 수 있게 설계
    관계
    호환되지 않는 인터페이스를 중간에 맞춤
    여러 알고리즘을 인터페이스로 캡슐화
    유연성 포인트
    기존 클래스 변경 없이 새 코드와 연결
    런타임에 행위를 쉽게 교체
    사용 위치
    주로 레거시 코드 재사용, 외부 라이브러리 연결 시
    동작 전략을 바꾸고 싶을 때, 유연한 설계 요구 시

    💡 간단한 비유

    패턴
    예시 비유
    어댑터
    110V 플러그를 220V에 꽂기 위한 변환기
    전략
    ‘결제 방식’을 런타임에 카드/페이코/카카오로 바꾸는 구조

    ✅ 핵심 요약

    비교 대상
    객체 어댑터
    클래스 어댑터
    전략 패턴
    구현 방법
    포함 (Composition)
    상속 (Inheritance)
    인터페이스 기반 전략 교체
    사용 목적
    인터페이스 호환
    인터페이스 호환
    행위 변경
    특징
    유연하고 범용적
    구조 간단하나 제한적
    실행 중 변경 유연
    Share article
    Contents
    ✅ Adapter Pattern실습1실습2실습3예제✅ 객체 어댑터 (Object Adapter) vs 클래스 어댑터 (Class Adapter)✅ 어댑터 패턴 vs 전략 패턴 비교

    parangdajavous

    RSS·Powered by Inblog