Zastosowanie
Fasada
(ang. Facade
) (wzorzec strukturalny) ujednolica oraz upraszcza dostęp do złożonego systemu lub jego części poprzez udostępnienie uporządkowanego interfejsu programistycznego. Wzorzec ten czerpię inspiracje z architektury - fasady, czyli głównej i często efektownej elewacji budynku, która pełni role reprezentacyjną. Wzorzec ten analogicznie pełni rolę “nakładki” dodając kolejny wyższy poziom abstrakcji. Nie rozszerza on zatem funkcjonalności systemu, ale sprawia, że użycie jego funkcji jest prostsze, a tworzony kod czytelniejszy. Fasada
sprawdza się wszędzie tam gdzie wykonanie operacji w systemie pociąga za sobą sekwencje wykonań operacji w różnych częściach systemu oraz w określonej kolejności. Jest często stosowanym wzorcem i stanowi nieodłączny element niejednego refaktoringu. Programista używający Fasady
nie musi wiedzieć jak dokładnie działa dana operacja, co jednak nie zwalnia go z czytania załączonej dokumentacji.
Ograniczenia
Fasada
może przechowywać referencje do obiektów, dzięki czemu istnieje silna pokusa, aby jednostkowe operacje mogły być również dostępne dla programisty. Takie podejście przeczy jednak idei wzorca, który ukrywa szczegóły implementacji. Przy projektowaniu Fasady
należy więc przeanalizować jakie jednostkowe operacje z jej obiektów powinny być dostępne w interfejsie, a które ukryte w taki sposób, aby zmiana stanu obiektów nie wpływała na poprawne działanie Fasady
. Co więcej Fasada
może przyjmować zbyt wielką odpowiedzialność i mieć za dużo zależności co prowadzi do stworzenia super klasy
(god object
).
Użycie
Można pokusić się o stwierdzenie, że praktycznie każde API
czy framework
jest Fasadą
, tzn. pełni rolę pośrednika między skomplikowanym system, a klientem zapewniając dostęp do prostych w użyciu lecz często skomplikowanych “w środku” funkcji. Fasada
jest zatem prawie wszędzie.
Implementacja
Tworzenie Fasady
sprowadza się przede wszystkim do agregacji powiązanych ze sobą obiektów oraz udostępnienie metod publicznych, które implementują szczegółowe wykonanie żądanej operacji.
Poniższy listing przedstawia przykładową implementacje Fasady
operującej na obiektach dwóch typów, klas Utils
oraz Enumach
.
Przykład
Aplikacja jest mockupem odtwarzacza muzyki (Klient), którzy korzysta z autorskiego MediaPlayer
(Fasada). Programista tworząc aplikacje odtwarzacza ma za zadanie przygotować zrozumiały interfejs, który pozwoli innemu programiście w prosty i krótki sposób wywołać podstawowe funkcje odtwarzacza takie jak play
, pause
, stop
, zmiana utworów czy regulacja głośności (metody Fasady). Wiedza na temat sposobu działania tak przygotowanego API nie jest niezbędna, aby programista mógł zaimplementować podstawowe funkcje odtwarzacza. W przypadku niejasności czy też zaawansowanych operacji powinien on sięgnąć do dokumentacji oraz kodu źródłowego. Odpowiednio zaprogramowany MediaPlayer
komunikuje się z różnymi innymi elementami systemu i modułami zewnętrznymi, deleguje im poszczególne zadania oraz zarządza całym procesem. Każdorazowe, ręczne tworzenie podstawowych funkcji odtwarzacza może być czasoschłonne, nieczytalne i wykryczać poza możliwości początkującego programisty, zatem zastosowanie Fasady
jest tu wysoce pożądane. Udostępnia ona w klarowny sposób funkcje systemu, ukrywając przy tym mechanizm działania. Poniższy listing przedstawia Fasadę MediaPlayer
.
Tak przygotowana Fasada
wraz z zaimplementowanymi modułami, umożliwia w łatwy sposób podpięcie akcji pod interfejs użytkownika co przekłada się również na czytelność kodu.
Biblioteki
Implementacja Fasady
jest tak różna jak realizacja danego problemu, w związku z czym nie ma jednej zasady jej tworzenia, a biblioteki usprawniające proces implementacji Fasady
w systemie nie mają racji bytu. Każda zewnętrzna biblioteka (np.: Retrofit
, Glide
) jest Fasadą
w tworzonym oprogramowaniu.