home > back-end > design-pattern > [design-pattern] facade 패턴

[design-pattern] facade 패턴
back-end design-pattern facade

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 패턴은 복잡한 시스템을 단순화하고, 내부 시스템에 대한 의존성을 줄이며, 클라이언트가 쉽게 사용할 수 있도록 편의성을 제공 해주는 구조적 디자인 패턴이다.