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
