퍼사드 패턴
개요
영화를 틀려면 스크린을 내리고 조명을 낮추고 플레이어를 시작해야 한다. 버튼을 누르는 쪽이 매번 장치와 순서를 알아야 한다면 같은 준비 코드가 여러 화면에 퍼진다.
퍼사드(Facade)는 복잡한 하위 시스템 앞에 단순한 진입점을 세우는 패턴이다. 호출부는 영화 시작을 요청하고, 퍼사드가 그 작업에 필요한 하위 기능들을 연결한다.
UML
호출부는 MovieNight를 사용한다. MovieNight가 Screen, Lights, Player를 참조하고 시작·종료 시 필요한 순서로 호출한다.
역사
GoF는 1994년 Design Patterns에서 Facade를 구조 패턴으로 정리했다. 하위 시스템의 여러 인터페이스를 더 쓰기 쉬운 상위 인터페이스로 묶는 데 초점을 둔다.1
왜 사용하는가?
호출부는 개별 장치의 이름과 준비 순서를 몰라도 된다. 순서가 달라지면 여러 화면 대신 이 진입점에서 수정한다.
퍼사드는 진입점이지 쓰레기통이 아니다. 모든 업무 규칙을 직접 들고 있거나 세부 제어가 필요한 호출부까지 한 메서드로 묶으면 큰 객체만 남을 수 있다. 하위 기능의 책임은 해당 모듈에 둔다.
예제
영화 시작 과정을 메서드 하나로 묶는다. 장치는 실제 하드웨어 대신 수행할 동작의 이름을 반환한다.
using System;
using System.Collections.Generic;
using System.Linq;
var night = new MovieNight(new Screen(), new Lights(), new Player());
Console.WriteLine(night.Start("Moon"));
sealed class Screen
{
public string Lower() => "screen down";
public string Raise() => "screen up";
}
sealed class Lights
{
public string Dim() => "lights dim";
public string Restore() => "lights normal";
}
sealed class Player
{
public string Play(string title) => "play " + title;
public string Stop() => "player stop";
}
sealed class MovieNight(Screen screen, Lights lights, Player player)
{
public string Start(string title) =>
string.Join(" -> ", screen.Lower(), lights.Dim(), player.Play(title));
public string Stop() =>
string.Join(" -> ", player.Stop(), lights.Restore(), screen.Raise());
}
screen down -> lights dim -> play MoonStart()가 정한 순서대로 결과가 나열된다. 실제 장치를 제어하는 코드와 호출 순서의 책임은 구별되어 있다.
범위 넓힌 예제
종료 작업도 같은 퍼사드에 두고, 시작 후 종료까지 호출한다.
using System;
using System.Collections.Generic;
using System.Linq;
var night = new MovieNight(new Screen(), new Lights(), new Player());
Console.WriteLine(night.Start("Moon"));
Console.WriteLine(night.Stop());
sealed class Screen
{
public string Lower() => "screen down";
public string Raise() => "screen up";
}
sealed class Lights
{
public string Dim() => "lights dim";
public string Restore() => "lights normal";
}
sealed class Player
{
public string Play(string title) => "play " + title;
public string Stop() => "player stop";
}
sealed class MovieNight(Screen screen, Lights lights, Player player)
{
public string Start(string title) =>
string.Join(" -> ", screen.Lower(), lights.Dim(), player.Play(title));
public string Stop() =>
string.Join(" -> ", player.Stop(), lights.Restore(), screen.Raise());
}
screen down -> lights dim -> play Moon
player stop -> lights normal -> screen up한 메서드로 묶었다고 여러 장치의 작업이 원자적으로 성공하는 것은 아니다. 실제 장치라면 중간 실패를 어떻게 알리고 복구할지 따로 정해야 한다. Query Facade는 이 진입점 아이디어를 조회 결과 구성에 적용한 별도의 문서에서 다룬다.
같이 보기
각 예제는 독립된 .NET 10 콘솔 프로그램이다.