// ❌ 나쁜 예: 클라이언트가 복잡한 서브시스템을 직접 다룸
public class Client {
public void compileCode(InputStream input, BytecodeStream output) {
// 클라이언트가 컴파일러의 모든 내부 구조를 알아야 함
Scanner scanner = new Scanner(input);
ProgramNodeBuilder builder = new ProgramNodeBuilder();
Parser parser = new Parser();
parser.Parse(scanner, builder);
RISCCodeGenerator generator = new RISCCodeGenerator(output);
ProgramNode parseTree = builder.GetRootNode();
parseTree.Traverse(generator);
}
}
문제점:
| 요소 | 역할 |
|---|---|
| Facade | 서브시스템의 복잡한 클래스들을 통합하는 간단한 인터페이스 제공 |
| Subsystem Classes | 실제 작업을 수행하는 개별 클래스들 (Facade를 알지 못함) |
| Client | Facade를 통해 서브시스템 사용 (서브시스템 직접 접근도 가능) |
| 구분 | Facade | Adapter |
|---|---|---|
| 목적 | 복잡한 인터페이스를 단순화 | 호환되지 않는 인터페이스를 변환 |
| 인터페이스 개수 | 여러 인터페이스 → 하나의 간단한 인터페이스 | 하나의 인터페이스 → 다른 인터페이스 |
| 기존 인터페이스 | 꼭 필요하지 않음 | 반드시 준수해야 할 인터페이스 존재 |
| 구분 | Facade | Mediator |
|---|---|---|
| 관계 방향 | 단방향 (Client → Facade → Subsystem) | 양방향 (Colleagues ↔ Mediator) |
| 인지 여부 | 서브시스템은 Facade를 모름 | Colleagues는 Mediator를 알고 있음 |
| 기능 추가 | 기능 추가 없음 (단순화만) | 새로운 협력 기능 추가 가능 |

Client (여러 개)
↓
Facade (단일 진입점)
↓ ↓ ↓
SubsystemA SubsystemB SubsystemC
↓ ↓ ↓
Class1 Class2 Class3
- 클라이언트는 Facade만 알면 됨
- Facade는 내부적으로 서브시스템 클래스들을 조율
- 고급 사용자는 원한다면 서브시스템에 직접 접근 가능
// ❌ Facade 없이 사용 - 너무 복잡함!
public class Client {
public void watchMovie() {
// 11단계의 복잡한 과정...
popper.on();
popper.pop();
lights.dim(10);
screen.down();
projector.on();
projector.setInput(dvd);
projector.wideScreenMode();
amp.on();
amp.setDvd(dvd);
amp.setSurroundSound();
amp.setVolume(5);
dvd.on();
dvd.play(movie);
}
}
// 서브시스템의 복잡한 클래스들
public class Amplifier {
public void on() { System.out.println("앰프 켜기"); }
public void setDvd(DvdPlayer dvd) { System.out.println("DVD 연결"); }
public void setSurroundSound() { System.out.println("서라운드 사운드 모드"); }
public void setVolume(int level) { System.out.println("볼륨: " + level); }
public void off() { System.out.println("앰프 끄기"); }
}
public class DvdPlayer {
public void on() { System.out.println("DVD 플레이어 켜기"); }
public void play(String movie) { System.out.println("재생: " + movie); }
public void stop() { System.out.println("DVD 정지"); }
public void eject() { System.out.println("DVD 꺼내기"); }
public void off() { System.out.println("DVD 플레이어 끄기"); }
}
public class Projector {
public void on() { System.out.println("프로젝터 켜기"); }
public void wideScreenMode() { System.out.println("와이드스크린 모드"); }
public void off() { System.out.println("프로젝터 끄기"); }
}
public class TheaterLights {
public void dim(int level) { System.out.println("조명 밝기: " + level + "%"); }
public void on() { System.out.println("조명 켜기"); }
}
public class Screen {
public void down() { System.out.println("스크린 내리기"); }
public void up() { System.out.println("스크린 올리기"); }
}
public class PopcornPopper {
public void on() { System.out.println("팝콘 기계 켜기"); }
public void pop() { System.out.println("팝콘 튀기는 중..."); }
public void off() { System.out.println("팝콘 기계 끄기"); }
}
// ✅ Facade: 복잡한 서브시스템을 간단한 인터페이스로 통합
public class HomeTheaterFacade {
// 서브시스템 컴포넌트들
private Amplifier amp;
private DvdPlayer dvd;
private Projector projector;
private TheaterLights lights;
private Screen screen;
private PopcornPopper popper;
// 생성자에서 모든 서브시스템 컴포넌트 초기화
public HomeTheaterFacade(Amplifier amp, DvdPlayer dvd,
Projector projector, TheaterLights lights,
Screen screen, PopcornPopper popper) {
this.amp = amp;
this.dvd = dvd;
this.projector = projector;
this.lights = lights;
this.screen = screen;
this.popper = popper;
}
// 간단한 메서드: 영화 보기 (내부적으로 복잡한 과정 처리)
public void watchMovie(String movie) {
System.out.println("\n영화 볼 준비 중...\n");
popper.on();
popper.pop();
lights.dim(10);
screen.down();
projector.on();
projector.wideScreenMode();
amp.on();
amp.setDvd(dvd);
amp.setSurroundSound();
amp.setVolume(5);
dvd.on();
dvd.play(movie);
System.out.println("\n준비 완료! 영화를 즐기세요!\n");
}
// 간단한 메서드: 영화 끝내기
public void endMovie() {
System.out.println("\n홈 시어터 종료 중...\n");
popper.off();
lights.on();
screen.up();
projector.off();
amp.off();
dvd.stop();
dvd.eject();
dvd.off();
System.out.println("\n홈 시어터 종료 완료!\n");
}
// 음악 듣기 기능도 추가 가능
public void listenToMusic(String album) {
System.out.println("\n음악 듣기 모드...\n");
lights.on();
amp.on();
amp.setVolume(5);
// ... 음악 관련 설정
}
}
public class HomeTheaterTest {
public static void main(String[] args) {
// 서브시스템 컴포넌트 생성
Amplifier amp = new Amplifier();
DvdPlayer dvd = new DvdPlayer();
Projector projector = new Projector();
TheaterLights lights = new TheaterLights();
Screen screen = new Screen();
PopcornPopper popper = new PopcornPopper();
// ✅ Facade 생성
HomeTheaterFacade homeTheater =
new HomeTheaterFacade(amp, dvd, projector, lights, screen, popper);
// 간단한 메서드 호출로 복잡한 과정 실행!
homeTheater.watchMovie("인셉션");
// ... 영화 감상 ...
homeTheater.endMovie();
}
}
영화 볼 준비 중...
팝콘 기계 켜기
팝콘 튀기는 중...
조명 밝기: 10%
스크린 내리기
프로젝터 켜기
와이드스크린 모드
앰프 켜기
DVD 연결
서라운드 사운드 모드
볼륨: 5
DVD 플레이어 켜기
재생: 인셉션
준비 완료! 영화를 즐기세요!
홈 시어터 종료 중...
팝콘 기계 끄기
조명 켜기
스크린 올리기
프로젝터 끄기
앰프 끄기
DVD 정지
DVD 꺼내기
DVD 플레이어 끄기
홈 시어터 종료 완료!
// 서브시스템 클래스들
class Scanner {
public Scanner(InputStream input) { /* ... */ }
}
class Parser {
public void Parse(Scanner scanner, ProgramNodeBuilder builder) { /* ... */ }
}
class ProgramNodeBuilder {
public ProgramNode GetRootNode() { /* ... */ return null; }
}
class RISCCodeGenerator {
public RISCCodeGenerator(BytecodeStream output) { /* ... */ }
}
class ProgramNode {
public void Traverse(CodeGenerator generator) { /* ... */ }
}
// ✅ Compiler Facade
class Compiler {
public Compiler() {}
// 단 하나의 간단한 메서드로 전체 컴파일 과정 수행
public void Compile(InputStream input, BytecodeStream output) {
Scanner scanner = new Scanner(input);
ProgramNodeBuilder builder = new ProgramNodeBuilder();
Parser parser = new Parser();
parser.Parse(scanner, builder);
RISCCodeGenerator generator = new RISCCodeGenerator(output);
ProgramNode parseTree = builder.GetRootNode();
parseTree.Traverse(generator);
}
}
// 클라이언트 사용
public class Main {
public static void main(String[] args) {
Compiler compiler = new Compiler();
// 복잡한 내부 과정은 신경 쓰지 않고 간단하게 사용!
compiler.Compile(inputStream, outputStream);
}
}
Facade Pattern은 Law of Demeter를 잘 따릅니다.
메서드 m은 다음 객체의 메서드만 호출해야 함:
1. 객체 자신 (this)
2. 메서드의 매개변수
3. 메서드 내에서 생성한 객체
4. 객체의 직접적인 컴포넌트
5. 전역 변수
// 두 개의 점(.) 사용 - 위반!
public float getTemp() {
Thermometer thermometer = station.getThermometer(); // 첫 번째 호출
return thermometer.getTemperature(); // 두 번째 호출
}
문제점:
station의 내부 구조(Thermometer)를 알아야 함Thermometer가 변경되면 이 코드도 수정 필요// 하나의 점(.) 사용 - Facade 역할!
public float getTemp() {
return station.getTemperature(); // station이 Facade처럼 동작
}
// Station 클래스 내부에서 처리
class Station {
private Thermometer thermometer;
public float getTemperature() {
return thermometer.getTemperature(); // 내부에서 처리
}
}
장점:
Station만 알면 됨Thermometer 구현이 변경되어도 클라이언트 코드는 안전| 요소 | 설명 |
|---|---|
| 목적 | 복잡한 서브시스템에 대한 간단한 인터페이스 제공 |
| 핵심 | 기능 추가 없이 사용성만 개선 |
| 관계 | Client → Facade → Subsystems (단방향) |
| 장점 | 낮은 결합도, 쉬운 사용, 서브시스템 변경에 강함 |
Facade가 필요한지 확인:
Facade 구현 시: