intro : facade 패턴에 대해서 알아보자
Facade 패턴이란 ?
Facade
란 프랑스어로 어떤 건물의 외벽을 뜻하는 말이다. 외벽을 뜻하는 단어로 이루어진 디자인 패턴은 어떤 패턴일까 ? 좀 더 자세히 알아보자면, 구조적 디자인 패턴
중 하나로 복잡한 서브시스템을 단순화하여 사용하는 패턴이라고 볼 수 있다. 이 패턴은 클라이언트가 복잡한 시스템이나 여러 클래스를 직접 다루지 않고, 단일 인터페이스
를 통해 시스템을 간단히 사용
할 수 있도록 해준다. 즉, 복잡한 내부 시스템을 숨기고
, 간단하고 일관된 인터페이스
를 제공
하는 것이 Facade 패턴의 핵심이라고 볼 수 있다.
Facade 패턴은 언제 사용하는가 ?
Facade 패턴은 복잡한 서브시스템의 인터페이스를 단순화하여 클라이언트가 쉽게 접근할 수 있도록 할 때 사용한다. 이 패턴은 클라이언트와 복잡한 서브시스템 간에 하나의 통합된 진입점을 제공함이 중요한 특징이다.
Facade 패턴 적용 전 (1)
어떠한 클라이언트는 아침에 일어나면 온도를 조절하고, 불을 켜고, 커피를 마신다. 이러한 행동은 다음과 같이 각 클래스의 메소드들로 나누어져 있으며, 객체를 생성하여 각각 호출하여야 한다.
public class Thermostat {
public void setTemperature(int temperature) {
System.out.println("Setting thermostat to " + temperature + " degrees.");
}
}
public class Lights {
public void on() {
System.out.println("Lights are on.");
}
public void off() {
System.out.println("Lights are off.");
}
}
public class CoffeeMaker {
public void brewCoffee() {
System.out.println("Brewing coffee.");
}
}
실제로 클라이언트는 각 클래스의 메소드들을 어떻게 호출해야 하는지 다음과 같은 코드를 보면 알 수 있다.
// Client code
public class Main {
public static void main(String[] args) {
Thermostat thermostat = new Thermostat(); // 온도 class 객체 생성
Lights lights = new Lights(); // 불 class 객체 생성
CoffeeMaker coffeeMaker = new CoffeeMaker(); // 커피 머신 class 객체 생성
thermostat.setTemperature(25); // 온도 25도 설정
lights.on(); // 불 전원 on
coffeeMaker.brewCoffee(); // 커미 끓이기
}
}
Facade 패턴 적용 후 (1)
기존에 각 클래스들로 나누어져 있던 것들을 하나로 모은 클래스 SmartHomeFacade
생성하여 클라이언트가 wakeUp
, leaveHome
메소드를 통해 위 3개의 클래스를 제어 할 수 있도록 기능을 제공한다.
public class SmartHomeFacade {
private Thermostat thermostat;
private Lights lights;
private CoffeeMaker coffeeMaker;
public SmartHomeFacade(Thermostat thermostat, Lights lights, CoffeeMaker coffeeMaker) {
this.thermostat = thermostat;
this.lights = lights;
this.coffeeMaker = coffeeMaker;
}
public void wakeUp() {
System.out.println("Waking up...");
thermostat.setTemperature(22);
lights.on();
coffeeMaker.brewCoffee();
}
public void leaveHome() {
System.out.println("Leaving home...");
thermostat.setTemperature(18);
lights.off();
}
}
클라이언트는 아래 코드에서 보듯이 Thermostat
Lights
CoffeeMaker
클래스들의 각 역할을 알지 못하여도, 일관된 인터페이스로 제공된 SmartHomeFacade
객체의 wakeUp
, leaveHome
메소드들을 통해 쉽고 간단하게 제어 할 수 있다. 이게 Facade 패턴의 장점이다.
// Client code
public class Main {
public static void main(String[] args) {
Thermostat thermostat = new Thermostat();
Lights lights = new Lights();
CoffeeMaker coffeeMaker = new CoffeeMaker();
SmartHomeFacade smartHome = new SmartHomeFacade(thermostat, lights, coffeeMaker);
smartHome.wakeUp();
smartHome.leaveHome();
}
}
Facade 패턴 적용 전 (2)
다른 예제로도 패턴 적용을 해보도록 하겠다. 다음과 같이 각 클래스가 FileReader
, FileWriter
, FileDeleter
3개로 나누어져 있다고 상황을 가정해 보자.
class FileReader {
public String readFile(String filePath)
throws IOException {
return new String(Files.readAllBytes(Paths.get(filePath)));
}
}
class FileWriter {
public void writeFile(String filePath, String content)
throws IOException {
Files.write(Paths.get(filePath), content.getBytes());
}
}
class FileDeleter {
public void deleteFile(String filePath)
throws IOException {
Files.delete(Paths.get(filePath));
}
}
이걸 만약에 클라이언트에서 각각의 클래스를 생성해서 사용하려면? 다음 코드와 같이 객체를 따로 생성해서 메소드를 각각의 클래스에 맞게 상황에 따로 호출해줘야 한다.
// Client code
public class Main {
public static void main(String[] args) throws IOException {
FileReader fileReader = new FileReader();
FileWriter fileWriter = new FileWriter();
FileDeleter fileDeleter = new FileDeleter();
String readFile = fileReader.readFile("test.txt");
fileWriter.writeFile("test.txt", "쓰고싶은 문자열");
fileDeleter.deleteFile("test.txt");
}
}
Facade 패턴 적용 후 (2)
Facade 패턴을 적용하면 내부 시스템의 클래스 객체를 생성하는 것 까지도 다음과 같이 처리 할 수 있다. 해당 객체를 생성하는 시점에서 필요한 서브시스템의 클래스들을 생성자에서 생성해버린다.
class FileSystemFacade {
private FileReader fileReader;
private FileWriter fileWriter;
private FileDeleter fileDeleter;
public FileSystemFacade() {
this.fileReader = new FileReader();
this.fileWriter = new FileWriter();
this.fileDeleter = new FileDeleter();
}
public String readFile(String filePath) {
try {
return fileReader.readFile(filePath);
} catch (IOException e) {
System.err.println("Error reading file: " + e.getMessage());
return null;
}
}
public boolean writeFile(String filePath, String content) {
try {
fileWriter.writeFile(filePath, content);
return true;
} catch (IOException e) {
System.err.println("Error writing file: " + e.getMessage());
return false;
}
}
public boolean deleteFile(String filePath) {
try {
fileDeleter.deleteFile(filePath);
return true;
} catch (IOException e) {
System.err.println("Error deleting file: " + e.getMessage());
return false;
}
}
}
클라이언트는 아래 코드에서 보듯이 FileSystemFacade
클래스의 객체만 생성하며 서브시스템의 복잡도는 줄이고 높은 편의성을 제공하는 것을 알 수 있다.
// Client code
public class Main {
public static void main(String[] args) {
FileSystemFacade fs = new FileSystemFacade();
// Write to file
boolean writeSuccess = fs.writeFile("test.txt", "Hello, Facade Pattern!");
System.out.println("File write success: " + writeSuccess);
// Read from file
String content = fs.readFile("test.txt");
System.out.println("File content: " + content);
// Delete file
boolean deleteSuccess = fs.deleteFile("test.txt");
System.out.println("File delete success: " + deleteSuccess);
}
}
Facade 패턴 요약
Facade
패턴은 복잡한 시스템을 단순화
하고, 내부 시스템에 대한 의존성을 줄이며
, 클라이언트가 쉽게
사용할 수 있도록 편의성을 제공 해주는 구조적 디자인 패턴
이다.