λ””μžμΈ νŒ¨ν„΄/GOF

πŸ’  μ–΄λŒ‘ν„°(Adaptor) νŒ¨ν„΄ - μ™„λ²½ λ§ˆμŠ€ν„°ν•˜κΈ°

인파_ 2023. 1. 25. 09:06

Adaptor-Pattern
Adaptor-Pattern

Adaptor Pattern

μ–΄λŒ‘ν„° νŒ¨ν„΄(Adaptor Pattern) μ΄λž€ 이름 κ·ΈλŒ€λ‘œ 클래슀λ₯Ό μ–΄λŒ‘ν„°λ‘œμ„œ μ‚¬μš©λ˜λŠ” ꡬ쑰 νŒ¨ν„΄μ΄λ‹€.

μ–΄λŒ‘ν„°λŠ” 우리 주변에도 많이 λ³Ό 수 μžˆλŠ” κ²ƒμœΌλ‘œμ„œ, λŒ€ν‘œμ μœΌλ‘œ 110V μ „μš© κ°€μ „μ œν’ˆμ— 220V μ–΄λŒ‘ν„°λ₯Ό λΌμ›Œ μ‚¬μš©ν•˜λŠ” κ±Έ λ“€ 수 μžˆλ‹€. 즉, μ„œλ‘œ ν˜Έν™˜μ΄ λ˜μ§€ μ•Šμ€ λ‹¨μžλ₯Ό μ–΄λŒ‘ν„°λ‘œ ν˜Έν™˜μ‹œμΌœ μž‘λ™μ‹œν‚€κ²Œλ” ν•˜λŠ” 것이 μ–΄λŒ‘ν„°μ˜ 역할인 것이닀.

Adaptor-Pattern
μ „μžκΈ°κΈ° λ‹¨μžλ₯Ό ν˜Έν™˜ν•˜κ²Œ ν•΄μ£ΌλŠ” μ–΄λŒ‘ν„°

이λ₯Ό 객체 지ν–₯ ν”„λ‘œκ·Έλž˜λ°μ— μ ‘λͺ©ν•΄λ³΄λ©΄, ν˜Έν™˜μ„±μ΄ μ—†λŠ” μΈν„°νŽ˜μ΄μŠ€ λ•Œλ¬Έμ— ν•¨κ»˜ λ™μž‘ν•  수 μ—†λŠ” ν΄λž˜μŠ€λ“€μ„ ν•¨κ»˜ μž‘λ™ν•΄μ£Όλ„λ‘ λ³€ν™˜ 역할을 ν•΄μ£ΌλŠ” 행동 νŒ¨ν„΄μ΄λΌκ³  보면 λœλ‹€.  예λ₯Όλ“€μ–΄ 기쑴에 μžˆλŠ” μ‹œμŠ€ν…œμ— μƒˆλ‘œμš΄ μ¨λ“œνŒŒν‹° 라이브러리λ₯Ό μΆ”κ°€ν•˜κ³  μ‹Άκ±°λ‚˜, Legacy μΈν„°νŽ˜μ΄μŠ€λ₯Ό μƒˆλ‘œμš΄ μΈν„°νŽ˜μ΄μŠ€λ‘œ κ΅μ²΄ν•˜λŠ” κ²½μš°μ— μ–΄λŒ‘ν„° νŒ¨ν„΄μ„ μ‚¬μš©ν•˜λ©΄ μ½”λ“œμ˜ μž¬μ‚¬μš©μ„±μ„ 높일 수 μžˆλ‹€.

Adaptor-Pattern
μ–΄λŒ‘ν„°λ₯Ό μ΄μš©ν•΄ κΈ°μ‘΄ μ‹œμŠ€ν…œμ— μ—…μ²΄μ˜ 클래슀λ₯Ό ν˜Έν™˜μ‹œμΌœ μ ‘λͺ©μ‹œν‚¨λ‹€

즉, μ–΄λŒ‘ν„°λž€ 이미 κ΅¬μΆ•λ˜μ–΄ μžˆλŠ” 것을 μƒˆλ‘œμš΄ 어떀것에 μ‚¬μš©ν• λ•Œ μ–‘ μͺ½ κ°„μ˜ ν˜Έν™˜μ„±μ„ μœ μ§€ν•΄ μ£ΌκΈ° μœ„ν•΄ μ‚¬μš©ν•˜λŠ” κ²ƒμœΌλ‘œμ„œ, κΈ°μ‘΄ μ‹œμŠ€ν…œμ—μ„œ μƒˆλ‘œμš΄ μ—…μ²΄μ—μ„œ μ œκ³΅ν•˜λŠ” κΈ°λŠ₯을 μ‚¬μš©ν•˜λ €κ³  ν• λ•Œ μ„œλ‘œ κ°„μ˜ μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ–΄λŒ‘ν„°λ‘œ μΌμΉ˜μ‹œμΌœμ€ŒμœΌλ‘œμ¨ ν˜Έν™˜μ„± 및 μ‹ κ·œ κΈ°λŠ₯ ν™•μž₯을 ν• μˆ˜ μžˆλ‹€κ³  보면 λœλ‹€.

μ–΄λŒ‘ν„°κ°€ Legacy μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ°μ‹Έμ„œ μƒˆλ‘œμš΄ μΈν„°νŽ˜μ΄μŠ€λ‘œ λ³€ν™˜ν•˜κΈ° λ•Œλ¬Έμ— Wrapper νŒ¨ν„΄μ΄λΌκ³ λ„ λΆˆλ¦¬μš΄λ‹€.

μ–΄λŒ‘ν„° νŒ¨ν„΄ ꡬ쑰

Adapter νŒ¨ν„΄μ—λŠ” κΈ°μ‘΄ μ‹œμŠ€ν…œμ˜ 클래슀λ₯Ό 상속(Inheritance) ν•΄μ„œ ν˜Έν™˜ μž‘μ—…μ„ 해주냐, ν•©μ„±(Composition) ν•΄μ„œ ν˜Έν™˜ μž‘μ—…μ„ 해주냐에 따라, 두 가지 νŒ¨ν„΄ λ°©λ²•μœΌλ‘œ λ‚˜λ‰˜κ²Œ λœλ‹€.

 

객체 μ–΄λŒ‘ν„° (Object Adaptor)

  • ν•©μ„±(Composition)된 λ§΄λ²„μ—κ²Œ μœ„μž„μ„ μ΄μš©ν•œ μ–΄λŒ‘ν„° νŒ¨ν„΄ (μΆ”μ²œ 🌟)
  • μžκΈ°κ°€ ν•΄μ•Ό ν•  일을 클래슀 맴버 객체의 λ©”μ†Œλ“œμ—κ²Œ λ‹€μ‹œ μ‹œν‚΄μœΌλ‘œμ¨ λͺ©μ μ„ λ‹¬μ„±ν•˜λŠ” 것을 μœ„μž„μ΄λΌκ³  ν•œλ‹€.
  • 합성을 ν™œμš©ν–ˆκΈ° λ•Œλ¬Έμ— λŸ°νƒ€μž„ 쀑에 Adaptee(Service)κ°€ κ²°μ •λ˜μ–΄ μœ μ—°ν•˜λ‹€.
  • κ·ΈλŸ¬λ‚˜ Adaptee(Service) 객체λ₯Ό ν•„λ“œ λ³€μˆ˜λ‘œ μ €μž₯ν•΄μ•Ό 되기 λ•Œλ¬Έμ— 곡간 차지 λΉ„μš©μ΄ λ“ λ‹€.

Object Adaptor

  • Adaptee(Service) : μ–΄λŒ‘ν„° λŒ€μƒ 객체. κΈ°μ‘΄ μ‹œμŠ€ν…œ / μ™ΈλΆ€ μ‹œμŠ€ν…œ / μ¨λ“œνŒŒν‹° 라이브러리
  • Target(Client Interface) : Adapter κ°€ κ΅¬ν˜„ν•˜λŠ” μΈν„°νŽ˜μ΄μŠ€.
  • Adapter : Client 와 Adaptee(Service) μ€‘κ°„μ—μ„œ ν˜Έν™˜μ„±μ΄ μ—†λŠ” λ‘˜μ„ μ—°κ²°μ‹œμΌœμ£ΌλŠ” 역할을 λ‹΄λ‹Ή.
    • Object Adaptor 방식에선 합성을 μ΄μš©ν•΄ κ΅¬μ„±ν•œλ‹€.
    • Adaptee(Service)λ₯Ό λ”°λ‘œ 클래슀 λ©€λ²„λ‘œ μ„€μ •ν•˜κ³  μœ„μž„μ„ 톡해 λ™μž‘μ„ λ§€μΉ˜μ‹œν‚¨λ‹€.
  • Client : κΈ°μ‘΄ μ‹œμŠ€ν…œμ„ μ–΄λŒ‘ν„°λ₯Ό 톡해 μ΄μš©ν•˜λ €λŠ” μͺ½. Client Interfaceλ₯Ό ν†΅ν•˜μ—¬ Serviceλ₯Ό μ΄μš©ν•  수 있게 λœλ‹€.
// Adaptee : ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ μ‚¬μš©ν•˜κ³  싢은 기쑴의 μ„œλΉ„μŠ€ (ν•˜μ§€λ§Œ ν˜Έν™˜μ΄ μ•ˆλ˜μ„œ λ°”λ‘œ μ‚¬μš© λΆˆκ°€λŠ₯)
class Service {

    void specificMethod(int specialData) {
        System.out.println("κΈ°μ‘΄ μ„œλΉ„μŠ€ κΈ°λŠ₯ 호좜 + " + specialData);
    }
}

// Client Interface : ν΄λΌμ΄μ–ΈνŠΈκ°€ μ ‘κ·Όν•΄μ„œ μ‚¬μš©ν•  κ³ μˆ˜μ€€μ˜ μ–΄λŒ‘ν„° λͺ¨λ“ˆ
interface Target {
    void method(int data);
}

// Adapter : Adaptee μ„œλΉ„μŠ€λ₯Ό ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ μ‚¬μš©ν•˜κ²Œ ν•  수 μžˆλ„λ‘ ν˜Έν™˜ 처리 ν•΄μ£ΌλŠ” μ–΄λŒ‘ν„°
class Adapter implements Target {
    Service adaptee; // composition으둜 Service 객체λ₯Ό 클래슀 ν•„λ“œλ‘œ

    // μ–΄λŒ‘ν„°κ°€ μΈμŠ€ν„΄μŠ€ν™”λ˜λ©΄ ν˜Έν™˜μ‹œν‚¬ κΈ°μ‘΄ μ„œλΉ„μŠ€λ₯Ό μ„€μ •
    Adapter(Service adaptee) {
        this.adaptee = adaptee;
    }

    // μ–΄λŒ‘ν„°μ˜ λ©”μ†Œλ“œκ°€ 호좜되면, Adaptee의 λ©”μ†Œλ“œλ₯Ό ν˜ΈμΆœν•˜λ„λ‘
    public void method(int data) {
        adaptee.specificMethod(data); // μœ„μž„
    }
}
class Client {
    public static void main(String[] args) {
        // 1. μ–΄λŒ‘ν„° 생성 (κΈ°μ‘΄ μ„œλΉ„μŠ€λ₯Ό 인자둜 λ°›μ•„ ν˜Έν™˜ μž‘μ—… 처리)
        Target adapter = new Adapter(new Service());

        // 2. Client Interfac의 μŠ€νŽ™μ— 따라 λ©”μ†Œλ“œλ₯Ό μ‹€ν–‰ν•˜λ©΄ κΈ°μ‘΄ μ„œλΉ„μŠ€μ˜ λ©”μ†Œλ“œκ°€ μ‹€ν–‰λœλ‹€.
        adapter.method(1);
    }
}

Object Adaptor

 

클래슀 μ–΄λŒ‘ν„° (Class Adaptor)

  • 클래슀 상속을 μ΄μš©ν•œ μ–΄λŒ‘ν„° νŒ¨ν„΄
  • Adaptee(Service)λ₯Ό μƒμ†ν–ˆκΈ° λ•Œλ¬Έμ— λ”°λ‘œ 객체 κ΅¬ν˜„μ—†μ΄ λ°”λ‘œ μ½”λ“œ μž¬μ‚¬μš©μ΄ κ°€λŠ₯ν•˜λ‹€.
  • 상속은 λŒ€ν‘œμ μœΌλ‘œ 기쑴에 κ΅¬ν˜„λœ μ½”λ“œλ₯Ό μž¬μ‚¬μš©ν•˜λŠ” λ°©μ‹μ΄μ§€λ§Œ, μžλ°”μ—μ„œλŠ” 닀쀑 상속 λΆˆκ°€ 문제 λ•Œλ¬Έμ— μ „λ°˜μ μœΌλ‘œ ꢌμž₯ν•˜μ§€λŠ” μ•ŠλŠ” 방법이닀.

Class Adaptor

  • Adaptee(Service) : μ–΄λŒ‘ν„° λŒ€μƒ 객체. κΈ°μ‘΄ μ‹œμŠ€ν…œ / μ™ΈλΆ€ μ‹œμŠ€ν…œ / μ¨λ“œνŒŒν‹° 라이브러리
  • Target(Cient Interface) : Adapter κ°€ κ΅¬ν˜„ν•˜λŠ” μΈν„°νŽ˜μ΄μŠ€.
  • Adapter : Client 와 Adaptee(Service) μ€‘κ°„μ—μ„œ ν˜Έν™˜μ„±μ΄ μ—†λŠ” λ‘˜μ„ μ—°κ²°μ‹œμΌœμ£ΌλŠ” 역할을 λ‹΄λ‹Ή.
    • Class Adaptor 방식에선 상속을 μ΄μš©ν•΄ κ΅¬μ„±ν•œλ‹€.
    • Existing Class와 Adaptee(Service) λ₯Ό λ™μ‹œμ— implements, extends ν•˜μ—¬ κ΅¬ν˜„ν•œλ‹€.
  • Client : κΈ°μ‘΄ μ‹œμŠ€ν…œμ„ μ–΄λŒ‘ν„°λ₯Ό 톡해 μ΄μš©ν•˜λ €λŠ” μͺ½. Client Interfaceλ₯Ό ν†΅ν•˜μ—¬ Serviceλ₯Ό μ΄μš©ν•  수 있게 λœλ‹€.
// Adaptee : ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ μ‚¬μš©ν•˜κ³  싢은 기쑴의 μ„œλΉ„μŠ€ (ν•˜μ§€λ§Œ ν˜Έν™˜μ΄ μ•ˆλ˜μ„œ λ°”λ‘œ μ‚¬μš© λΆˆκ°€λŠ₯)
class Service {

    void specificMethod(int specialData) {
        System.out.println("κΈ°μ‘΄ μ„œλΉ„μŠ€ κΈ°λŠ₯ 호좜 + " + specialData);
    }
}

// Client Interface : ν΄λΌμ΄μ–ΈνŠΈκ°€ μ ‘κ·Όν•΄μ„œ μ‚¬μš©ν•  κ³ μˆ˜μ€€μ˜ μ–΄λŒ‘ν„° λͺ¨λ“ˆ
interface Target {
    void method(int data);
}

// Adapter : Adaptee μ„œλΉ„μŠ€λ₯Ό ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ μ‚¬μš©ν•˜κ²Œ ν•  수 μžˆλ„λ‘ ν˜Έν™˜ 처리 ν•΄μ£ΌλŠ” μ–΄λŒ‘ν„°
class Adapter extends Service implements Target {

    // μ–΄λŒ‘ν„°μ˜ λ©”μ†Œλ“œκ°€ 호좜되면, λΆ€λͺ¨ 클래슀 Adaptee의 λ©”μ†Œλ“œλ₯Ό 호좜
    public void method(int data) {
        specificMethod(data);
    }
}
class Client {
    public static void main(String[] args) {
        // 1. μ–΄λŒ‘ν„° 생성
        Target adapter = new Adapter();

        // 2. μΈν„°νŽ˜μ΄μŠ€μ˜ μŠ€νŽ™μ— 따라 λ©”μ†Œλ“œλ₯Ό μ‹€ν–‰ν•˜λ©΄ κΈ°μ‘΄ μ„œλΉ„μŠ€μ˜ λ©”μ†Œλ“œκ°€ μ‹€ν–‰λœλ‹€.
        adapter.method(1);
    }
}

Class Adaptor


μ–΄λŒ‘ν„° νŒ¨ν„΄ νŠΉμ§•

 

νŒ¨ν„΄ μ‚¬μš© μ‹œκΈ°

  • λ ˆκ±°μ‹œ μ½”λ“œλ₯Ό μ‚¬μš©ν•˜κ³  μ‹Άμ§€λ§Œ μƒˆλ‘œμš΄ μΈν„°νŽ˜μ΄μŠ€κ°€ λ ˆκ±°μ‹œ μ½”λ“œμ™€ ν˜Έν™˜λ˜μ§€ μ•Šμ„ λ•Œ
  • 이미 λ§Œλ“  κ²ƒμ„ μž¬μ‚¬μš©ν•˜κ³ μž ν•˜λ‚˜ μ΄ μž¬μ‚¬μš© κ°€λŠ₯ν•œ λΌμ΄λΈŒλŸ¬λ¦¬λ₯Ό μˆ˜μ •ν•  μˆ˜ μ—†μ„ λ•Œ
  • 이미 λ§Œλ“€μ–΄μ§„ 클래슀λ₯Ό μƒˆλ‘œμš΄ μΈν„°νŽ˜μ΄μŠ€(API)에 맞게 κ°œμ‘°ν• λ•Œ
  • μ†Œν”„νŠΈμ›¨μ–΄μ˜ ꡬ 버전과 μ‹  버전을 κ³΅μ‘΄μ‹œν‚€κ³  μ‹Άμ„λ•Œ
기쑴의 클래슀λ₯Ό μƒˆλ‘œμš΄ μΈν„°νŽ˜μ΄μŠ€μ— 맞게 κ°œμ‘°ν•˜κ³  μ‹Άμ„λ•Œ, μš°λ¦¬λŠ” κΈ°μ‘΄ 클래슀의 μ†ŒμŠ€λ₯Ό λ°”κΎΈμ–΄ μˆ˜μ •μ„ ν•΄μ™”μ—ˆλ‹€. ν•˜μ§€λ§Œ μ΄λŸ°μ‹μœΌλ‘œ ν”„λ‘œκ·Έλž¨μ„ μš΄μš©ν•˜λ©΄ 이미 λ™μž‘ ν…ŒμŠ€νŠΈκ°€ λλ‚œ 기쑴의 클래슀λ₯Ό λ‹€μ‹œ ν•œλ²ˆ ν…ŒμŠ€νŠΈ ν•΄μ•Ό ν•œλ‹€. 만일 클래슀 μ†ŒμŠ€κ°€ λͺ‡μ²œμ€„이라면 μš΄μš©ν•˜κΈ° κ½€λ‚˜ νž˜λ“€ 것이닀.
μ΄λŸ¬ν•œ κ΄€μ μ—μ„œ μ–΄λŒ‘ν„°(Adapter) νŒ¨ν„΄μ€ 기쑴의 클래슀λ₯Ό μˆ˜μ •ν•˜μ§€ μ•Šκ³  μƒˆλ‘œμš΄ μΈν„°νŽ˜μ΄μŠ€μ— 맞게 ν˜Έν™˜μž‘μ—…μ„ μ€‘κ³„ν•˜μ—¬ ν•΄μ£ΌλŠ” 것이닀.

 

νŒ¨ν„΄ μž₯점

  • ν”„λ‘œκ·Έλž¨μ˜ κΈ°λ³Έ λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ—μ„œ μΈν„°νŽ˜μ΄μŠ€ λ˜λŠ” 데이터 λ³€ν™˜ μ½”λ“œλ₯Ό 뢄리할 수 있기 λ•Œλ¬Έμ— 단일 μ±…μž„ 원칙(SRP)을 λ§Œμ‘±ν•œλ‹€.
  • κΈ°μ‘΄ 클래슀 μ½”λ“œλ₯Ό 건듀지 μ•Šκ³  ν΄λΌμ΄μ–ΈνŠΈ μΈν„°νŽ˜μ΄μŠ€λ₯Ό 톡해 μ–΄λŒ‘ν„°μ™€ μž‘λ™ν•˜κΈ° λ•Œλ¬Έμ— 개방 폐쇄 원칙(OCP)을 λ§Œμ‘±ν•œλ‹€.
  • 만일 μΆ”κ°€λ‘œ ν•„μš”ν•œ λ©”μ†Œλ“œκ°€ μžˆλ‹€λ©΄ μ–΄λŒ‘ν„°μ— λΉ λ₯΄κ²Œ λ§Œλ“€ 수 μžˆλ‹€. λ§Œμ•½ 버그가 λ°œμƒν•΄λ„ 기쑴의 ν΄λž˜μŠ€μ—λŠ” 버그가 μ—†μœΌλ―€λ‘œ Adapter μ—­ν• μ˜ 클래슀λ₯Ό μ€‘μ μ μœΌλ‘œ μ‘°μ‚¬ν•˜λ©΄ 되고, ν”„λ‘œκ·Έλž¨ 검사도 μ‰¬μ›Œμ§„λ‹€.

 

νŒ¨ν„΄ 단점

  • μƒˆλ‘œμš΄ μΈν„°νŽ˜μ΄μŠ€μ™€ μ–΄λŒ‘ν„° 클래슀 μ„ΈνŠΈλ₯Ό λ„μž…ν•΄μ•Ό ν•˜κΈ° λ•Œλ¬Έμ— μ½”λ“œμ˜ λ³΅μž‘μ„±μ΄ μ¦κ°€ν•œλ‹€. 
  • λ•Œλ‘œλŠ” 직접 μ„œλΉ„μŠ€(Adaptee) 클래슀λ₯Ό λ³€κ²½ν•˜λŠ”κ²ƒμ΄ κ°„λ‹¨ν• μˆ˜ μžˆλŠ” κ²½μš°κ°€ 있기 λ•Œλ¬Έμ— μ‹ μ€‘νžˆ μ„ νƒν•˜μ—¬μ•Ό ν•œλ‹€.

예제λ₯Ό 톡해 μ•Œμ•„λ³΄λŠ” Adaptor νŒ¨ν„΄

 

ν”„λ‘œκ·Έλž¨μ˜ 엔진을 κ΅μ²΄ν•˜κ³  ν˜Έν™˜μ‹œν‚€κΈ°

우리 νšŒμ‚¬μ—μ„œλŠ” A νšŒμ‚¬μ—μ„œ κ°œλ°œν•œ Sort엔진 μ†”λ£¨μ…˜μ„ κ΅¬λ§€ν•΄μ„œ 우리 νšŒμ‚¬μ˜ Sort머신에 νƒ‘μž¬ν•˜μ—¬ μ‚¬μš©ν•˜κ³  μžˆμ–΄μ™”κ³  ν•œλ‹€.

interface ISortEngine {
    void setList(); // μ •λ ¬ν•  리슀트
    void sort(); // μ •λ ¬ μ•Œκ³ λ¦¬μ¦˜
    void reverseSort(); // μ—­μˆœ μ •λ ¬ μ•Œκ³ λ¦¬μ¦˜
    void printSortListPretty(); // μ •λ ¬λœ 리슀트λ₯Ό 예쁘게 좜λ ₯
}

class A_SortEngine implements ISortEngine {
    public void setList() {}
    public void sort() {}
    public void reverseSort() {}
    public void printSortListPretty() {}
}
// Client 역할을 ν•˜λŠ” 클래슀 : Sort 엔진 객체λ₯Ό λ°›μ•„ μ‹€ν–‰
class SortingMachine {
    ISortEngine engine;

    void setEngine(ISortEngine engine) { this.engine = engine; }

    void sortingRun() {
        engine.setList();

        engine.sort();
        engine.printSortListPretty();

        engine.reverseSort();
        engine.printSortListPretty();
    }

    public static void main(String[] args) {
        SortingMachine machine = new SortingMachine();
        machine.setEngine(new A_SortEngine());
        machine.sortingRun();
    }
}

그런데 A νšŒμ‚¬μ˜ Sort엔진 μ„±λŠ₯이 영 κ·Έλž˜μ„œ νƒμƒ‰ν•œ κ²°κ³Ό μ„±λŠ₯이 쒋은 B νšŒμ‚¬μ˜ Sortμ—”μ§„μœΌλ‘œ κ΅μ²΄ν•˜κΈ°λ‘œ κ²°μ •ν–ˆλ‹€. κ·Έλž˜μ„œ B νšŒμ‚¬λ‘œλΆ€ν„° 엔진 λͺ…μ„Έμ„œλ₯Ό λ°›μ•„μ™”λ”λ‹ˆ, 그런데 λ‹€μŒκ³Ό 같이 κΈ°μ‘΄ Sortμ—”μ§„κ³ΌλŠ” λ™μž‘ λ©”μ„œλ“œ μ‹œκ·Έλ‹ˆμ²˜κ°€ λ‹¬λžμœΌλ©° 심지어 μ§€μ›ν•˜μ§€ μ•ŠλŠ” λ©”μ„œλ“œ(printSortListPretty)도 μ‘΄μž¬ν–ˆλ‹€.

class B_SortEngine {
    public void setList() {} // μ •λ ¬ν•  리슀트
    public void sorting(boolean isReverse) {} // μ •λ ¬ / μ—­μˆœ μ •λ ¬ μ•Œκ³ λ¦¬μ¦˜ (νŒŒλΌλ―Έν„°λ‘œ μˆœμ„œ κ²°μ •)
}

우리 νšŒμ‚¬μ˜ Sortμ—”μ§„μ—μ„œ μΈν„°νŽ˜μ΄μŠ€λ₯Ό λŒμ–΄λ‹€ μ“°κ³  μžˆμ—ˆκΈ° λ•Œλ¬Έμ— B_Sort 엔진에 μΈν„°νŽ˜μ΄μŠ€λ₯Ό implements 해도 ν˜Έν™˜μ΄ λ˜μ§€ μ•Šμ•„ ν¬λ‚˜ν° μˆ˜μ • μž‘μ—…μ΄ ν•„μš”λΆˆκ°€κ²° ν•˜λ©°, 특히 printSortListPretty λ©”μ„œλ“œ λΆ€μž¬λŠ” 쑰금 치λͺ…μ μ΄μ—ˆλŠ”λ° μ™œλƒν•˜λ©΄ 이 λ©”μ„œλ“œλŠ” ꡉμž₯히 이쁘게 μ •λ ¬λœ 리슀트λ₯Ό 좜λ ₯ν•΄μ„œ λ§ˆμŒμ— λ“€μ—ˆκΈ° λ•Œλ¬Έμ΄λ‹€. ν•˜μ§€λ§Œ B νšŒμ‚¬μ˜ 엔진 μ„±λŠ₯이 A νšŒμ‚¬ 보닀 4λ°°λ‚˜ 빨랐기 λ•Œλ¬Έμ— λΉ„μš©μ΄ 듀어도 μ–΄λ–»κ²Œλ“  ꡐ체해야 ν•˜λŠ” 상황이닀.

 

ν˜Έν™˜μ„±μ— λ¬Έμ œκ°€ μžˆλŠ” μ½”λ“œ ❌

일단 κ°€μž₯ 직관적인 해결법은 SortingMachine 클래슀λ₯Ό ν†΅μ§œ μˆ˜μ •ν•΄μ„œ μ μ‘ν•˜λŠ” 방법이닀. κ·Έλž˜μ„œ 머리λ₯Ό μ₯μ–΄μ§  κ²°κ³Ό κΈ°μ‘΄ μ‚¬μš©ν•˜λ˜ μΈν„°νŽ˜μ΄μŠ€ λ‚΄λΆ€λ₯Ό μ‚­μ œν•˜μ—¬ 마컀 μΈν„°νŽ˜μ΄μŠ€ μš©λ„λ‘œλ§Œ μ΄μš©ν•˜κ³ , Sortλ¨Έμ‹ μ—μ„œ λ‹€μš΄ μΊμŠ€νŒ…μœΌλ‘œ 톡해 λ‘˜μ΄ ν˜Έν™˜μ„ μ‹œμΌœ μ£Όμ—ˆλ‹€.

interface ISortEngine {
    // 두 엔진 κ°„μ˜ 간극이 λ§žμ§€ μ•ŠκΈ° λ•Œλ¬Έμ— μ–΄μ©”μˆ˜μ—†μ΄ μ‚­μ œν•΄λ²„λ¦¬κ³ , 마컀 μΈν„°νŽ˜μ΄μŠ€ μš©λ„λ‘œλ§Œ μ‚¬μš©
}

class A_SortEngine implements ISortEngine {
    public void setList() {}
    public void sort() {}
    public void reverseSort() {}
    public void printSortListPretty() {}
}

class B_SortEngine implements ISortEngine {
    public void setList() {} // μ •λ ¬ν•  리슀트
    public void sorting(boolean isReverse) {} // μ •λ ¬ / μ—­μˆœ μ •λ ¬ μ•Œκ³ λ¦¬μ¦˜ (νŒŒλΌλ―Έν„°λ‘œ μˆœμ„œ κ²°μ •)
}
// Client 역할을 ν•˜λŠ” 클래슀 : Sort 엔진 객체λ₯Ό λ°›μ•„ μ‹€ν–‰
class SortingMachine {
    // ISortEngine engine; - ν•„μš” μ—†μ–΄μ Έμ„œ deprecated

    // void setEngine(ISortEngine engine) { this.engine = engine; } - ν•„μš” μ—†μ–΄μ Έμ„œ deprecated

    void sortingRun() {
        A_SortEngine Aengine = (A_SortEngine) this.engine;
        B_SortEngine Bengine = (B_SortEngine) this.engine;

        Bengine.setList();
        Bengine.sorting(false);

        Aengine.setList();
        Aengine.printSortListPretty();
    }

    public static void main(String[] args) {
        SortingMachine machine = new SortingMachine();
        // machine.setEngine(new A_SortEngine());
        machine.sortingRun();
    }
}

이 μ½”λ“œλ₯Ό 더 이상 μœ μ§€ 보수 ν•  것이 μ•„λ‹ˆλΌλ©΄ μ΄λŒ€λ‘œ μ μš©ν•˜λ©΄ λœλ‹€. ν•˜μ§€λ§Œ 객체 지ν–₯ ν”„λ‘œκ·Έλž˜λ°μ—μ„œ κ³ μˆ˜μ€€μ΄ μ•„λ‹Œ μ €μˆ˜μ€€ λͺ¨λ“ˆλ‘œ μ˜μ‘΄ν•΄μ„œ λ‘œμ§μ„ μ§œλŠ”κ±΄ μ’‹μ§€μ•ŠμœΌλ©°, 만일 λ‚˜μ€‘μ— λ‹€λ₯Έ νšŒμ‚¬μ˜ μ—”μ§„μœΌλ‘œ 또 ꡐ체해야할 κ²½μš°κ°€ μ˜¨λ‹€λ©΄ μ•ˆκ·Έλž˜λ„ μ–΅μ§€λ‘œ ν˜Έν™˜μ‹œν‚€λŠλΌ λœ―μ–΄κ³ μΉœ μ½”λ“œ λ‹€μ‹œ 전체λ₯Ό λœ―μ–΄κ³ μ³μ•Ό 될지도 λͺ¨λ₯Έλ‹€. λ”°λΌμ„œ μ΄λŸ¬ν•œ λ…Έκ°€λ‹€ 슀러운 방법은 μ’‹μ§€μ•Šλ‹€.

 

객체 μ–΄λŒ‘ν„° νŒ¨ν„΄μ„ μ μš©ν•œ μ½”λ“œ βœ”οΈ

μ–΄λŒ‘ν„° νŒ¨ν„΄μ€ ν΄λΌμ΄μ–ΈνŠΈκ°€ μ‚¬μš©ν•˜λŠ” μΈν„°νŽ˜μ΄μŠ€λŠ” μ •ν•΄μ Έ μžˆλŠ”λ°, μ μš©ν•  μ½”λ“œ(Adaptee)κ°€ ν•΄λ‹Ή μΈν„°νŽ˜μ΄μŠ€λ₯Ό λ”°λ₯΄μ§€ μ•Šμ„λ•Œ ν΄λΌμ΄μ–ΈνŠΈμ™€ Adaptee μ‚¬μ΄μ˜ 간극을 μ–΄λŒ‘ν„°λ‘œ λ§€κΏ”μ„œ Adapteeλ₯Ό μž¬μ‚¬μš©ν•  수 μžˆλ„λ‘ ν•œλ‹€.

λ”°λΌμ„œ 우리 νšŒμ‚¬μ˜ Sortμ—”μ§„μ—μ„œ μ΄μš©ν•˜λ˜ μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ†λŒ€μ§€ μ•Šκ³  λ³„λ„μ˜ μ–΄λŒ‘ν„° SortengineAdaptor 클래슀λ₯Ό λ§Œλ“€μ–΄μ„œ ν˜Έν™˜ μž‘μ—…μ„ μ‹œμΌœμ€„ 것이닀.

Adaptor-Pattern

// 기쑴의 μΈν„°νŽ˜μ΄μŠ€λŠ” μ†λŒ€μ§€ μ•ŠλŠ”λ‹€
interface ISortEngine {
    void setList(); // μ •λ ¬ν•  리슀트
    void sort(); // μ •λ ¬ μ•Œκ³ λ¦¬μ¦˜
    void reverseSort(); // μ—­μˆœ μ •λ ¬ μ•Œκ³ λ¦¬μ¦˜
    void printSortListPretty(); // μ •λ ¬λœ 리슀트λ₯Ό 예쁘게 좜λ ₯
}

class A_SortEngine implements ISortEngine {
    public void setList() {}
    public void sort() {}
    public void reverseSort() {}
    public void printSortListPretty() {}
}

class B_SortEngine {
    public void setList() {} // μ •λ ¬ν•  리슀트
    public void sorting(boolean isReverse) {} // μ •λ ¬ / μ—­μˆœ μ •λ ¬ μ•Œκ³ λ¦¬μ¦˜ (νŒŒλΌλ―Έν„°λ‘œ μˆœμ„œ κ²°μ •)
}
// 객체 μ–΄λŒ‘ν„°λ₯Ό κ΅¬μ„±ν•œλ‹€.
class SortEngineAdaptor implements ISortEngine {
    // (두 엔진을 composition ν•˜μ—¬ 이용)
    A_SortEngine Aengine;
    B_SortEngine Bengine;

    SortEngineAdaptor(A_SortEngine Aengine, B_SortEngine Bengine) {
        this.Aengine = Aengine;
        this.Bengine = Bengine;
    }

    @Override
    public void setList() {
        Bengine.setList();
    }

    @Override
    public void sort() {
        Bengine.sorting(false); // λ©”μ„œλ“œ μ‹œκ·Έλ‹ˆμ²˜κ°€ 달라고 μœ„μž„μ„ 톡해 ν˜Έν™˜ μž‘μ—…
    }

    @Override
    public void reverseSort() {
        Bengine.sorting(true); // λ©”μ„œλ“œ μ‹œκ·Έλ‹ˆμ²˜κ°€ 달라고 μœ„μž„μ„ 톡해 ν˜Έν™˜ μž‘μ—…
    }

    @Override
    public void printSortListPretty() {
        Aengine.printSortListPretty(); // B 엔진에 μ—†λŠ” κΈ°λŠ₯을 A μ—”μ§„μœΌλ‘œ μ‹€ν–‰
    }
}
// Client 역할을 ν•˜λŠ” 클래슀 : Sort 엔진 객체λ₯Ό λ°›μ•„ μ‹€ν–‰
class SortingMachine {
    ISortEngine engine;

    void setEngine(ISortEngine engine) { this.engine = engine; }

    void sortingRun() {
        engine.setList();

        engine.sort();
        engine.printSortListPretty();

        engine.reverseSort();
        engine.printSortListPretty();
    }

    public static void main(String[] args) {
        // ν΄λΌμ΄μ–ΈνŠΈμ˜ 머신에 원본 엔진 λŒ€μ‹  μ–΄λŒ‘ν„°λ₯Ό ν• λ‹Ήν•œλ‹€.
        ISortEngine adaptor = new SortEngineAdaptor(new A_SortEngine(), new B_SortEngine());
        SortingMachine machine = new SortingMachine();
        machine.setEngine(adaptor);

        machine.sortingRun();
    }
}

κΈ°μ‘΄ ν΄λΌμ΄μ–ΈνŠΈ μ½”λ“œλŠ” 건듀지 μ•Šμ€μ±„, 객체 ν• λ‹Ήλ§Œ μ–΄λŒ‘ν„° 객체λ₯Ό λ„£μœΌλ©΄ μˆ˜μ •μ—†μ΄ B 엔진 이용이 κ°€λŠ₯ν•΄μ§€λŠ”κ±Έ λ³Ό 수 μžˆλ‹€. λ‚˜μ€‘μ— C 엔진, D μ—”μ§„μœΌλ‘œ κ΅μ²΄ν•œλ‹€κ³  ν•˜λ”λΌλ„ μ–΄λŒ‘ν„° 클래슀만 적절히 μ†λ§Œ 봐주면 되기 λ•Œλ¬Έμ— μœ μ§€λ³΄μˆ˜κ°€ μš©μ΄ν•΄μ§„λ‹€.

Adaptor-Pattern

이 μ˜ˆμ œμ—μ„  기쑴의 μΈν„°νŽ˜μ΄μŠ€λ₯Ό μž¬ν™œμš© ν•œλ‹€λŠ” μ»¨μ…‰μœΌλ‘œ μ ‘κ·Όν•΄μ„œ μ–΄λŒ‘ν„°λ₯Ό κ΅¬μ„±ν•˜μ˜€λ‹€. ν•˜μ§€λ§Œ 만일 μƒˆλ‘œμš΄ λ‹€λ₯Έ μΈν„°νŽ˜μ΄μŠ€ λͺ…μ„Έκ°€ ν•„μš”ν•˜λ‹€λ©΄, μƒˆ μΈν„°νŽ˜μ΄μŠ€λ₯Ό λ§Œλ“€κ³  μ–΄λŒ‘ν„°λ‘œ μœ„μž„μ„ 톡해 ν˜Έν™˜μž‘μ—…μ„ ν•΄μ£Όλ©΄ 되기 λ•Œλ¬Έμ— μ–΄λ–€μ‹μœΌλ‘œ μ ‘κ·Όν•˜λ“  상관 μ—†λ‹€.

 

클래슀 μ–΄λŒ‘ν„° νŒ¨ν„΄μ„ μ μš©ν•œ μ½”λ“œ β­•

μ΄λ²ˆμ—” 클래슀 μ–΄λŒ‘ν„° ν˜•μ‹μœΌλ‘œ ꡬ성해보면 λ‹€μŒκ³Ό 같이 λœλ‹€. 상속 ꡬ쑰λ₯Ό 톡해 ν™•μ‹€νžˆ μ‹¬ν”Œν•˜κ²Œ λ³΄μΌμˆ˜λŠ” μžˆμ§€λ§Œ μžλ°”μ—μ„œλŠ” 단일 μƒμ†λ§Œ κ°€λŠ₯ν•˜κΈ° λ•Œλ¬Έμ— 만일 μ—¬λŸ¬κ°œμ˜ Adapteeλ₯Ό ν˜Έν™˜μž‘μ—…μ„ ν•΄μ€˜μ•Ό ν•œλ‹€λ©΄ ν•œκ³„μ μ΄ μ‘΄μž¬ν•œλ‹€.

Adaptor-Pattern

// 클래슀 μ–΄λŒ‘ν„°λ₯Ό κ΅¬μ„±ν•œλ‹€.
class SortEngineAdaptor extends B_SortEngine implements ISortEngine {

    @Override
    public void setList() {
        super.setList();
    }

    @Override
    public void sort() {
        sorting(false);
    }

    @Override
    public void reverseSort() {
        sorting(true);
    }

    @Override
    public void printSortListPretty() {
        // A_SortEngine 클래슀의 원본 printSortListPretty λ©”μ„œλ“œμ˜ μ•Œκ³ λ¦¬μ¦˜ λ‘œμ§μ„ κ·ΈλŒ€λ‘œ 볡뢙 및 κ΅¬ν˜„
    }
}
// Client 역할을 ν•˜λŠ” 클래슀 : Sort 엔진 객체λ₯Ό λ°›μ•„ μ‹€ν–‰
class SortingMachine {
    ISortEngine engine;

    void setEngine(ISortEngine engine) { this.engine = engine; }

    void sortingRun() {
        engine.setList();

        engine.sort();
        engine.printSortListPretty();

        engine.reverseSort();
        engine.printSortListPretty();
    }

    public static void main(String[] args) {
        // ν΄λΌμ΄μ–ΈνŠΈμ˜ 머신에 원본 엔진 λŒ€μ‹  μ–΄λŒ‘ν„°λ₯Ό ν• λ‹Ήν•œλ‹€.
        ISortEngine adaptor = new SortEngineAdaptor();
        SortingMachine machine = new SortingMachine();
        machine.setEngine(adaptor);

        machine.sortingRun();
    }
}

μ‹€λ¬΄μ—μ„œ μ°Ύμ•„λ³΄λŠ” Adaptor νŒ¨ν„΄

 

Java

  • java.util.Arrays 의 asList()
  • java.util.Collections μ˜ list()
  • java.util.Collections μ˜ enumeration()
  • java.io.InputStreamReader(InputStream) (returns a Reader)
  • java.io.OutputStreamWriter(OutputStream) (returns a Writer)
  • javax.xml.bind.annotation.adapters.XmlAdapter μ˜ marshal() and unmarshal()

 

InputStreamReader

μ½˜μ†”μ—μ„œ μž…λ ₯을 λ°›κ³  μ‹Άμ„λ•Œ Scanner ν΄λž˜μŠ€μ™€ κ°€μž₯ 많이 μ‚¬μš©ν•˜λŠ” 것이 BufferedReader 인데, μ•„λž˜μ™€ 같이 μƒμ„±μž 인자둜 InpuStreamReader 객체λ₯Ό λ„£μ–΄ μ‚¬μš©ν•˜λŠ” 것을 λ³Έ 적이 μžˆμ„ 것이닀.

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

이 μ½”λ“œμ˜ 핡심은 input을 ν–‰ν•˜λŠ” System.in 을 BufferedReader 객체에 μ‚¬μš©ν•˜κ³  싢은데 이 쀑간을 InpuStreamReader κ°€ μ–΄λŒ‘ν„° 역할을 ν•˜κ³  μžˆλŠ” 것이닀.

λ¨Όμ € BufferedReader ν΄λž˜μŠ€λ₯Ό μ‚΄νŽ΄λ³΄λ©΄ μƒμ„±μžμ— μ•„λž˜μ™€ 같이 Reader νƒ€μž…을 λ°›λŠ”κ±Έ λ³Ό 수 μžˆλ‹€.

adaptor-inputstream

ν•˜μ§€λ§Œ System 클래슀의 in ν•„λ“œλ₯Ό 보면 InputStream νƒ€μž…μœΌλ‘œ κ΅¬μ„±λ˜μ–΄ μžˆλ‹€. 즉, μ„œλ‘œ νƒ€μž…μ΄ λ§žμ§€ μ•Šμ•„ BufferedReader(System.in) κ³Ό 같이 μ‚¬μš©ν• μˆ˜ μ—†λŠ” 것이닀.

adaptor-inputstream

κ·Έλž˜μ„œ 이 λ‘˜μ„ μ—°κ²°μ‹œμΌœ μ£ΌλŠ” μ–΄λŒ‘ν„°κ°€ InputStreamReader ν΄λž˜μŠ€μ΄λ‹€.

InputStreamReader ν΄λž˜μŠ€λŠ” μƒμ„±μžλ‘œ InputStream νƒ€μž…μ„ λ°›κ³  μΈμŠ€ν„΄μŠ€ν™” ν•˜λ©°, InputStreamReader 클래슀 μžμ²΄κ°€ Reader 좔상 클래슀λ₯Ό μƒμ†ν•˜κ³  있기 λ•Œλ¬Έμ— μ—…μΊμŠ€νŒ…μœΌλ‘œ λ³€ν™˜ν•˜μ—¬ BufferedReader의 μƒμ„±μž 인자둜 λ“€μ–΄κ°€κ²Œ ν•΄μ£ΌλŠ” 것이닀.

adaptor-inputstream

이 ꡬ쑰λ₯Ό UML 둜 보면 μ•„λž˜μ™€ κ°™κ²Œ λœλ‹€. BufferedReader ν΄λž˜μŠ€μ™€ InputStreamReader ν΄λž˜μŠ€λŠ” Reader 좔상 클래슀λ₯Ό λ™μ‹œμ— 상속받고 μžˆλ‹€.

InputStreamReader ν΄λž˜μŠ€λŠ” InputStream νƒ€μž…μ„ 받을 수 μžˆλŠ” μƒμ„±μžλ₯Ό 가지고 μžˆμœΌλ―€λ‘œ, System.in 을 InputStreamReader μƒμ„±μžμ— λ„˜κ²¨ μΈμŠ€ν„΄μŠ€ν™” ν•˜λ©΄, 같은 Reader λΆ€λͺ¨ 클래슀λ₯Ό μƒμ†ν•˜κ³  μžˆλŠ” BufferedReader의 μƒμ„±μžμ˜ 인자둜 λ„˜κ²¨μ€„μˆ˜ 있기 λ•Œλ¬Έμ—, 마치 System.in 을 BufferedReader에 넣은 κ²ƒμ²˜λŸΌ ν˜Έν™˜ 역할을 InputStreamReader κ°€ μ–΄λŒ‘ν„°λ‘œμ„œ ν–‰ν•˜κ³  μžˆλ‹€κ³  λ³Ό 수 μžˆλ‹€.

adaptor-inputstream

λ”°λΌμ„œ μ •λ¦¬ν•˜λ©΄ InputStreamReader ν΄λž˜μŠ€λ₯Ό Adapter, System.in μ„ Adaptee, Reader λ₯Ό Target Interface 라고 ν•  수 μžˆλ‹€.

 

Arrays의 asList()

기쑴의 λ°°μ—΄(Array)을 리슀트(List)둜 λ³€ν™˜ & ν˜Έν™˜ μž‘μ—…μ„ ν•΄μ£ΌλŠ” Arrays.asList() 도 μ–΄μ°Œλ³΄λ©΄ μ–΄λŒ‘ν„°λΌκ³  λ³Ό 수 μžˆλ‹€.

public static void main(String[] args) {
	// String λ°°μ—΄ : adaptee κΈ°μ‘΄ μ„œλΉ„μŠ€
    String[] s = {"a", "b", "c"};
    
    // List<String> : target μΈν„°νŽ˜μ΄μŠ€
    List<String> list
    
    // Arrays.asList() : adapter κΈ°μ‘΄ μ„œλΉ„μŠ€ 배열을 리슀트둜 λ³€ν™˜ & ν˜Έν™˜ν•˜κ²Œ ν•΄μ£ΌλŠ” μ—­ν• 
    list = Arrays.asList(s);
}

 

Collections의 enumeration()

μ—­μ‹œλ‚˜ λ¦¬μŠ€νŠΈμ™€ Enumeration κ°„μ˜ λ³€ν™˜ & ν˜Έν™˜ μž‘μ—…μ„ ν•΄μ£ΌλŠ” Collections 의 정적 λ©”μ„œλ“œλ„ μ–΄λŒ‘터라고 λ³Ό 수 μžˆλ‹€.

public static void main(String[] args) {

	// 리슀트 : adaptee κΈ°μ‘΄ μ„œλΉ„μŠ€
	List<String> list = new ArrayList<>();
    list.add("a");
    list.add("b");
    list.add("c");
    
    // Enumeration<String> : target μΈν„°νŽ˜μ΄μŠ€
    Enumeration<String> enum
    
    // adapter κΈ°μ‘΄ μ„œλΉ„μŠ€ 배열을 리슀트둜 λ³€ν™˜ & ν˜Έν™˜ν•˜κ²Œ ν•΄μ£ΌλŠ” μ—­ν• 
    enum = Collections.enermeration(list); // list → enermeration
    ArrayList<String list2 = Collections.list(enum) // enermeration → list
}

# 참고자료

μ½”λ”©μœΌλ‘œ ν•™μŠ΅ν•˜λŠ” GoF의 λ””μžμΈ νŒ¨ν„΄ - λ°±κΈ°μ„ 

https://refactoring.guru/design-patterns/adapter

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