목차
- 1. Facade 패턴이란?
- 2. 왜 Facade 패턴이 필요한가?
- 3. 패턴 구조
- 4. 구현 예시
- 5. 실제 적용 사례
- 6. 실천 가이드 및 주의사항
1. Facade 패턴이란?
Facade 패턴은 복잡한 서브시스템의 여러 인터페이스를 간편하게 묶어주는 구조적 디자인 패턴입니다. 고수준 인터페이스를 통해 사용자에게 복잡성을 숨기는 것이 핵심입니다. 이 패턴은 다음과 같은 실제 사례에서 자주 활용됩니다:- Spring JDBC의 JdbcTemplate
- SLF4J 로깅 인터페이스
- Spring의 ApplicationContext
- 다양한 유틸리티 클래스
2. 왜 Facade 패턴이 필요한가?
이 패턴은 다음의 문제를 해결합니다:- 복잡성 감소: 서브시스템 내부 로직을 외부에서 숨기고 단순한 API를 제공
- 의존성 분리: 클라이언트는 서브시스템의 세부 사항을 알지 않아도 됨
- 계층 구조: 서브시스템의 다양한 계층에 맞춤형 인터페이스 제공 가능
- 의존성 최소화: 클라이언트는 단일 인터페이스만 의존
3. 패턴 구조
UML 다이어그램 생략
핵심 구성 요소
- Facade(인터페이스): 서브시스템의 요청을 적절한 객체에게 위임
- SubSystem(서브시스템): 실제 기능을 구현하며 Facade와 무관
- 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. 실천 가이드 및 주의사항
- 디자인 원칙: 단일 책임 원칙 준수, 인터페이스 사용 권장
- 성능 고려: 지연 초기화, 연결 캐싱, 자원 관리
- 확장성: 다양한 모드 지원, 구성 파일 활용
- 예외 처리: 일관된 오류 피드백, 자원 정확한 해제