Fasada

Głównym celem wzorca projektowego Fasada jest uproszczenie komunikacji między klientem a złożonym systemem. W dużych aplikacjach, gdzie logika biznesowa rozproszona jest między wieloma komponentami, Fasada oferuje pojedynczy interfejs, który ukrywa złożoność systemu wewnętrznego.

Dzięki Fasadzie klient nie musi znać szczegółów działania poszczególnych klas – wystarczy, że użyje interfejsu udostępnionego przez Fasadę.

Rozważmy aplikację Report Generator służącą do zarządzania dokumentami. Aplikacja składa się z kilku różnych klas i metod, które wywołane w odpowiedniej kolejności pozwalają wygenerować raport.

Typ wyliczeniowy Role przechowuje przewidziane w aplikacji role użytkowników:

public enum Role {
    ADMIN,
    USER;
}

Klasa User reprezentuje użytkownika który próbuje wygenerować dokument i musi zostać autoryzowany:

public class User {
    private String username;
    private Role role;

    public User(String username, Role role) {
        this.username = username;
        this.role = role;
    }

    public Role getRole() {
        return role;
    }
}

Klasa Report reprezentuje raport który ma zostać wygenerowany:

public class Report {
    private String content;

    Report(String content) { this.content = content; }

    public void exportToPDF() {
        System.out.println("Eksportowanie raportu do PDF");
        System.out.println(content);
    }
}

Klasa AuthService odpowiada za autoryzację użytkownika:

public class AuthService {
    public boolean hasAccess(User user) {
        return "ADMIN".equals(user.getRole());
    }
}

Klasa DataService generuje dane do raportu:

public class DataService {
    public String fetchDataForReport() {
        return "Dane finansowe Q2: [przychody, koszty, zysk...]";
    }
}

Wreszcie klasa ReportService służy do generowania raportów i zwraca obiekt klasy Report:

public class ReportService {
    public Report generateReport(String data) {
        return new Report("Raport wygenerowany na podstawie:\n" + data);
    }
}

Z aplikacją łączy się inna aplikacja – Klient. Klient jest zewnętrzną aplikacją która łączy się Generatorem Raportów i generuje raport:

public class Client {
    public static void main (String[] args) {
        User admin = new User("Jan", Role.ADMIN);

        AuthService authService = new AuthService();
        DataService dataService = new DataService();
        ReportService reportService = new ReportService();

        if (authService.hasAccess(admin)) {
            String data = dataService.fetchDataForReport();
            Report report = reportService.generateReport(data);
            report.exportToPDF();
        } else {
            System.out.println("Brak uprawnień do wygenerowania raportu.");
        }
    }
}

Klasa Client musi mieć wiedzę na temat działania aplikacji Report Generator, poszczególnych klas i metod. Client musi wiedzieć w jakiej kolejności wywoływać metody w celu wygenerowania raportu. Wreszcie Client ma dostęp do klas aplikacji. Fasada udostępnia Klientowi jeden interfejs ukrywając szczegóły implementacji.

Implementacja wzorca Fasada

Logika wygenerowanie raportu zostaje przeniesiona z klasy Client do klasy ReportFacade. Klasa ReportFacade ma jeden interfejs, generateReport(). Klient będzie korzystał z tego interfejsu:

public class ReportFacade {

    private AuthService authService = new AuthService();
    private DataService dataService = new DataService();
    private ReportService reportService = new ReportService();

    public void generateReport (User user) {
        if (authService.hasAccess(user)) {
            String data = dataService.fetchDataForReport();
            Report report = reportService.generateReport(data);
            report.exportToPDF();
        } else {
            System.out.println("Brak uprawnień do wygenerowania raportu.");
        }
    }
}

Dostęp do wszystkich pozostałych klas i metod zostaje zablokowany. W Java można przenieść klasy do oddzielnej paczki i usunąć modyfikator dostępu public. Dzięki temu dostęp do nich będzie miała tylko klasa ReportFacade, ale Client już nie.

Klasa Client wywołuje teraz tylko interfejs generateReport() z Fasady. W tym przypadku nie można ukryć tylko klasy User – Klient musi mieć do niej dostęp.

public class Client {
    public static void main (String[] args) {
        User admin = new User("Jan", Role.ADMIN);

        ReportFacade reportFacade = new ReportFacade();
        reportFacade.generateReport(admin);
    }
}

Klient nie musi już wiedzieć w jakiej kolejności wywoływać poszczególne metody. Cała logika generowania raportu jest przed nim ukryta. Klient musi tylko znać jeden interfejs.

Podsumowanie

Wzorzec Fasada to elegancki sposób na porządkowanie skomplikowanych systemów i oferowanie intuicyjnych, spójnych punktów dostępu. Fasada izoluje klienta od systemu czyli izoluje warstwę logiki biznesowej od wewnętrznych metod. Dzięki temu kod jest bezpieczniejszy i łatwiejszy do modyfikacji.

Fasada jest powszechnie stosowana w warstwie serwisowej aplikacji webowych i integracjach z zewnętrznymi usługami. Fasada pełni rolę bramy – spina wiele klas i komponentów w jeden logiczny punkt wejścia.


Wzorzec Fasada jest podobny do wzorca Adapter, ale różni się od niego intencją:

  • celem wzorca Fasada jest zapewnienie klientowi uproszczonego interfejsu dla danego systemu lub podsystemu,
  • celem wzorca Adapter jest modyfikacja interfejsu tak żeby dopasować go do potrzeb klienta