[CS] GoF 패턴 - Adapter, Facade 패턴

Sungjin Cho·2025년 3월 27일
1

CS

목록 보기
8/9
post-thumbnail

Adapter 패턴

Adapter 패턴은 서로 호환되지 않는 인터페이스를 가진 클래스들이 함께 동작할 수 있도록 변환해주는 구조적 패턴이다. 마치 전자기기의 어댑터처럼, 한쪽의 인터페이스를 다른 쪽에서 사용할 수 있는 형태로 맞춰주는 역할을 한다.

언제 사용할까?

  • 기존에 존재하는 클래스를 사용하고 싶지만, 그 인터페이스가 현재 시스템과 맞지 않을 때
  • 이미 작성된 코드를 수정하지 않고 새로운 요구사항에 맞춰 재사용하고 싶을 때 (예를 들어 이미 잘 작동하는 코드를 수정하기엔 위험도가 있으니, 기존 클래스는 그대로 유지하며 그 클래스의 메서드를 가져와 새로운 요구사항에 맞게 수정)
  • 서로 다른 라이브러리나 프레임워크를 통합할 때.

활용 방법

  • Target: 클라이언트가 기대하는 인터페이스 정의
  • Adaptee: 기존에 존재하는, 호환되지 않는 인터페이스를 가진 클래스
  • Adapter: Adapatee를 Target 인터페이스에 맞게 변환하는 중간 클래스

두 방식

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

예제 - 기존 LegacyPrinter가 있고, 새로운 시스템은 Printer 인터페이스를 요구할 때

Printer 인터페이스에 맞는 Adapter인 PrinterAdapter 클래스를 만든다. 내부에서 LegacyPrinter 를 활용하여 print 메서드를 구현한다.

// Target 인터페이스
interface Printer {
    void print(String message);
}

// Adaptee (기존 클래스)
class LegacyPrinter {
    public void printOldWay(String text) {
        System.out.println("Legacy Printer: " + text);
    }
}

// Adapter (객체 어댑터 방식)
class PrinterAdapter implements Printer {
    private LegacyPrinter legacyPrinter;

    public PrinterAdapter(LegacyPrinter legacyPrinter) {
        this.legacyPrinter = legacyPrinter;
    }

    @Override
    public void print(String message) {
        legacyPrinter.printOldWay(message); // Adaptee의 메서드 호출
    }
}

// 사용 예시
public class Main {
    public static void main(String[] args) {
        LegacyPrinter legacyPrinter = new LegacyPrinter();
        Printer printer = new PrinterAdapter(legacyPrinter);
        printer.print("Hello, Adapter Pattern!"); // 출력: Legacy Printer: Hello, Adapter Pattern!
    }
}

설명

  • PrinterAdapter는 LegacyPrinter를 감싸고, 클라이언트가 요구하는 print 메서드를 호출하면 printOldWay로 변환해준다.
  • 기존 코드를 수정하지 않고도 새로운 인터페이스에 맞춰 사용할 수 있다.

Facade 패턴

Facade 패턴은 복잡한 서브시스템을 단순화된 단일 인터페이스로 감싸서 클라이언트가 쉽게 사용할 수 있게 해주는 구조적 패턴이다. "외관"이라는 뜻처럼, 내부의 복잡성을 숨기고 깔끔한 외부를 제공한다.

언제 사용할까?

  • 복잡한 서브시스템을 단순화해서 사용하고 싶을 때.
  • 클라이언트와 서브시스템 간의 결합도를 낮추고 싶을 때.
  • 계층화된 구조에서 특정 계층에 접근을 간소화하고 싶을 때.

활용 방법

  • Facade: 서브시스템의 기능을 단순화된 메서드로 제공하는 클래스
  • Subsystem: 복잡한 내부 로직을 가진 클래스들로, Facade가 이를 호출.
  • 클라이언트는 Facade만 사용하며 내부 구조를 알 필요가 없음

예제 - 홈 시어터 시스템을 제어하는 복잡한 서브시스템을 단순화

// 서브시스템 클래스들
class Audio {
    public void turnOn() { System.out.println("Audio On"); }
    public void setVolume(int level) { System.out.println("Volume set to " + level); }
    public void turnOff() { System.out.println("Audio Off"); }
}

class Light {
    public void dim(int level) { System.out.println("Light dimmed to " + level); }
    public void turnOff() { System.out.println("Light Off"); }
}

class Projector {
    public void turnOn() { System.out.println("Projector On"); }
    public void setInput(String input) { System.out.println("Input set to " + input); }
    public void turnOff() { System.out.println("Projector Off"); }
}

// Facade 클래스
class HomeTheaterFacade {
    private Audio audio;
    private Light light;
    private Projector projector;

    public HomeTheaterFacade(Audio audio, Light light, Projector projector) {
        this.audio = audio;
        this.light = light;
        this.projector = projector;
    }

    public void watchMovie(String movie) {
        System.out.println("Preparing to watch " + movie + "...");
        light.dim(10);
        projector.turnOn();
        projector.setInput("HDMI");
        audio.turnOn();
        audio.setVolume(5);
        System.out.println("Enjoy the movie!");
    }

    public void endMovie() {
        System.out.println("Shutting down...");
        audio.turnOff();
        projector.turnOff();
        light.turnOff();
    }
}

// 사용 예시
public class Main {
    public static void main(String[] args) {
        Audio audio = new Audio();
        Light light = new Light();
        Projector projector = new Projector();

        HomeTheaterFacade homeTheater = new HomeTheaterFacade(audio, light, projector);
        homeTheater.watchMovie("Inception");
        System.out.println("-----");
        homeTheater.endMovie();
    }
}

설명

  • HomeTheaterFacade는 복잡한 서브시스템(Audio, Light, Projector)을 단순화된 watchMovie와 endMovie 메서드로 제공한다.
  • 클라이언트는 각 장비를 직접 제어할 필요 없이 Facade만 호출하면 된다.

Adapter vs Facade 비교

  • 목적:
    • Adapter: 인터페이스 호환성 해결.
    • Facade: 복잡성 숨기고 단순화.
  • 구성:
    • Adapter: 주로 하나의 클래스를 변환.
    • Facade: 여러 서브시스템을 통합.
  • 사용 예시:
    • Adapter: 레거시 코드 재사용.
    • Facade: 복잡한 API 단순화.

0개의 댓글