Facade 패턴: 서브시스템의 통합 인터페이스 제공

목차

  • 1. Facade 패턴이란?
  • 2. 왜 Facade 패턴이 필요한가?
  • 3. 패턴 구조
  • 4. 구현 예시
  • 5. 실제 적용 사례
  • 6. 실천 가이드 및 주의사항

1. Facade 패턴이란?

Facade 패턴은 복잡한 서브시스템의 여러 인터페이스를 간편하게 묶어주는 구조적 디자인 패턴입니다. 고수준 인터페이스를 통해 사용자에게 복잡성을 숨기는 것이 핵심입니다. 이 패턴은 다음과 같은 실제 사례에서 자주 활용됩니다:
  • Spring JDBC의 JdbcTemplate
  • SLF4J 로깅 인터페이스
  • Spring의 ApplicationContext
  • 다양한 유틸리티 클래스

2. 왜 Facade 패턴이 필요한가?

이 패턴은 다음의 문제를 해결합니다:
  1. 복잡성 감소: 서브시스템 내부 로직을 외부에서 숨기고 단순한 API를 제공
  2. 의존성 분리: 클라이언트는 서브시스템의 세부 사항을 알지 않아도 됨
  3. 계층 구조: 서브시스템의 다양한 계층에 맞춤형 인터페이스 제공 가능
  4. 의존성 최소화: 클라이언트는 단일 인터페이스만 의존

3. 패턴 구조

UML 다이어그램 생략

핵심 구성 요소

  1. Facade(인터페이스): 서브시스템의 요청을 적절한 객체에게 위임
  2. SubSystem(서브시스템): 실제 기능을 구현하며 Facade와 무관
  3. Client(클라이언트): Facade를 통해 서브시스템 접근

4. 구현 예시

스마트 홈 시스템 예제


// 서브시스템: 조명 제어
public class HomeLight {
    public void activate() {
        System.out.println("조명 켜기");
    }
    
    public void deactivate() {
        System.out.println("조명 끄기");
    }
    
    public void adjustBrightness() {
        System.out.println("밝기 조절");
    }
}

// 서브시스템: 온도 조절
public class ClimateControl {
    public void activate() {
        System.out.println("에어컨 켜기");
    }
    
    public void deactivate() {
        System.out.println("에어컨 끄기");
    }
    
    public void setTemp(int degree) {
        System.out.println("온도 설정: " + degree + "도");
    }
}

// 서브시스템: 오디오 시스템
public class AudioSystem {
    public void activate() {
        System.out.println("오디오 켜기");
    }
    
    public void deactivate() {
        System.out.println("오디오 끄기");
    }
    
    public void adjustVolume(int level) {
        System.out.println("볼륨 조절: " + level);
    }
    
    public void playMedia() {
        System.out.println("미디어 재생");
    }
}

// Facade: 홈 자동화 컨트롤러
public class HomeAutomationController {
    private final HomeLight light;
    private final ClimateControl climate;
    private final AudioSystem audio;
    
    public HomeAutomationController() {
        this.light = new HomeLight();
        this.climate = new ClimateControl();
        this.audio = new AudioSystem();
    }
    
    // 홈 모드
    public void setHomeMode() {
        System.out.println("홈 모드 실행:");
        light.activate();
        climate.activate();
        climate.setTemp(26);
        audio.activate();
        audio.adjustVolume(30);
        audio.playMedia();
    }
    
    // 외출 모드
    public void setAwayMode() {
        System.out.println("외출 모드 실행:");
        light.deactivate();
        climate.deactivate();
        audio.deactivate();
    }
    
    // 수면 모드
    public void setSleepMode() {
        System.out.println("수면 모드 실행:");
        light.adjustBrightness();
        climate.setTemp(24);
        audio.adjustVolume(10);
    }
}

// 사용 예
public class HomeAutomationDemo {
    public static void main(String[] args) {
        HomeAutomationController controller = new HomeAutomationController();
        
        System.out.println("===== 홈 모드 =====");
        controller.setHomeMode();
        
        System.out.println("\n===== 수면 모드 =====");
        controller.setSleepMode();
        
        System.out.println("\n===== 외출 모드 =====");
        controller.setAwayMode();
    }
}

5. 실제 적용 사례

5.1 데이터베이스 액세스 인터페이스


// 서브시스템: 연결 관리
public class ConnectionManager {
    public Connection establishConnection() {
        System.out.println("데이터베이스 연결 생성");
        return null; // 실제 구현은 생략
    }
    
    public void terminateConnection(Connection conn) {
        System.out.println("데이터베이스 연결 종료");
    }
}

// 서브시스템: 쿼리 실행
public class QueryExecutor {
    public void runQuery(String sql) {
        System.out.println("쿼리 실행: " + sql);
    }
    
    public void runUpdate(String sql) {
        System.out.println("업데이트 실행: " + sql);
    }
}

// 서브시스템: 결과 변환
public class ResultConverter {
    public <T> List<T> convertResults(ResultSet rs, Class<T> type) {
        System.out.println("결과 매핑");
        return new ArrayList<>();
    }
}

// Facade: 데이터 액세스 래퍼
public class DataAccessWrapper {
    private final ConnectionManager manager;
    private final QueryExecutor executor;
    private final ResultConverter converter;
    
    public DataAccessWrapper() {
        this.manager = new ConnectionManager();
        this.executor = new QueryExecutor();
        this.converter = new ResultConverter();
    }
    
    public <T> List<T> fetchList(String sql, Class<T> type) {
        Connection conn = null;
        try {
            conn = manager.establishConnection();
            executor.runQuery(sql);
            return converter.convertResults(null, type);
        } finally {
            if (conn != null) {
                manager.terminateConnection(conn);
            }
        }
    }
    
    public int modifyData(String sql) {
        Connection conn = null;
        try {
            conn = manager.establishConnection();
            executor.runUpdate(sql);
            return 1; // 가상 성공 반환
        } finally {
            if (conn != null) {
                manager.terminateConnection(conn);
            }
        }
    }
}

6. 실천 가이드 및 주의사항

  • 디자인 원칙: 단일 책임 원칙 준수, 인터페이스 사용 권장
  • 성능 고려: 지연 초기화, 연결 캐싱, 자원 관리
  • 확장성: 다양한 모드 지원, 구성 파일 활용
  • 예외 처리: 일관된 오류 피드백, 자원 정확한 해제

태그: DesignPattern Facade java SoftwareArchitecture ObjectOrientedDesign

8월 18일 03:07에 게시됨