Wzorzec projektowy Fasada - okładka

Wzorzec projektowy Fasada

Data publikacji Kategorie Czysty kod

Ten wpis jest jednym z serii wpisów o wzorcach projektowych. Zachęcam do zapoznania się z pozostałymi wpisami dotyczących wzorców projektowych:

Tym razem wezmę na tapet wzorzec projektowy Fasada.

Charakterystyka wzorca Fasada

Wzorzec ten jest jednym z wzorców opisanych w książce Design Patterns: Elements of Reusable Object-Oriented Software autorstwa bandy czworga (Gang of Four) – Gamma Erich, Helm Richard, Johnson Ralph oraz Vlissides John. Autorzy przypisali go do kategorii wzorców strukturalnych.

Tak jak w wielu innych wzorcach, tak i w przypadku Fasady łatwo można odnaleźć analogie w świecie rzeczywistym. Fasadą, z którą z pewnością się spotkałeś/aś jest bankomat. Bankomat jest interfejsem pośredniczącym między bankiem, dzięki któremu można:

  • wypłacić środki z konta za pomocą karty płatniczej,
  • wypłacić środki z konta za pomocą kodu BLIK,
  • sprawdzić stan konta.

Dzięki bankomatowi – Fasadzie, cała komunikacja między użytkownikiem a bankiem jest ukryta. Użytkownik nie musi wiedzieć, jak działa BLIK, nie musi przepisywać danych z karty, ani nawet wiedzieć do jakiego oddziału banku jest przypisany. Interakcja z użytkownikiem ograniczona jest do absolutnego minimum.

Takie same założenia zostały przyjęte dla wzorca projektowego Fasada. Ma ona za zadanie ukryć wszelkie szczegóły implementacyjne i detale, które nie są do niczego potrzebne użytkownikowi. Jednocześnie Fasada ma zadanie wystawić przyjazny interfejs dla użytkownika docelowego.

Praktyczny przykład

Mając już za sobą teoretyczny wstęp, czas przejść do praktyki. W tej części artykułu przedstawię Ci praktyczne zastosowanie wzorca Fasada. Postawionym zadaniem będzie zaprojektowanie aplikacji, która ma jedną publiczną metodę, która oczekuje podania treści w formacie kodu HTML oraz oczekiwanego formatu pliku wynikowego. Rezultatem zwróconym z omawianej metody ma być plik w podanym formacie. Diagram klas UML dla omawianego rozwiązana wygląda następująco:

Wzorzec Fasada - diagram UML

Przedstawione rozwiązanie pozwala na wygenerowanie dokumentu w formatach: DOCX, PDF i Markdown. Interfejs ogranicza interakcję z użytkownikiem do wymagań określonych wcześniej – jedna metoda z dwoma parametrami. Dzięki temu użytkownik nie musi wiedzieć absolutnie nic o tym jak aplikowane są przykładowo czcionki czy zdjęcia w dokumentach w wymienionych formatach. Powyższy diagram klas UML zaimplementowany w TypeScript wygląda następująco:


interface IPrinter {
    getFormat(): string;
    print( content: string ): Promise<Buffer>;
}

class MarkdownPrinter implements IPrinter {
    private readonly _format: string = 'md';

    public getFormat(): string {
        return this._format;
    }

    public print( content: string ): Promise<Buffer> {
        // ... magic
    }
}

class DOCXPrinter implements IPrinter {
    private readonly _format: string = 'docx';

    public getFormat(): string {
        return this._format;
    }

    public print( content: string ): Promise<Buffer> {
        // ... magic
    }
}

class PDFPrinter implements IPrinter {
    private readonly _format: string = 'pdf';

    public getFormat(): string {
        return this._format;
    }

    public print( content: string ): Promise<Buffer> {
        // ... magic
    }
}

class DocumentFacade {
    private readonly _printers: IPrinter[];

    public constructor() {
        this._printers = [
            new DOCXPrinter(),
            new MarkdownPrinter(),
            new PDFPrinter()
        ];
    } 

    public printDocument( format: string, content: string ): Promise<Buffer> {
        const printer: IPrinter | undefined = this._printers.find( printer => printer.getFormat() === format );

        if ( !printer ) {
            throw new Error( 'Document format not supported.' );
        }

        return printer.print( content );
    }
}

Powyższy kod w formie interaktywnej znajdziesz pod tym linkiem. W dalszych etapach rozwoju tego programu nic nie stoi na przeszkodzie by przykładowo podpisywać czy szyfrować generowany dokument poprzed dodatkową metodę w klasie DocumentFacade lub dodatkowy parametr w metodzie printDocument.

Analogie do innych wzorców projektowych

  • Różnica między Fasadą a Proxy jest to, że celem Proxy jest pośredniczenie między użytkownikiem a kodem docelowym. Przykładowo, chcąc wykonać zapytanie HTTP o obecną temperaturę powietrza w podanej lokalizacji, w przypadku Proxy, oczekiwałbym interefejsu pozwalającego zdefiniować URL, metodę, nagłówki itp. Samo Proxy dodawałoby tylko nagłówki uwierzytelniające. Natomiast w przypadku Fasady zaimplementowałbym metodę do pobierania temperatury, która wymaga jedynie podania miasta dla którego wykonane ma być zapytanie. Użytkownik nawet nie wiedziałby o tym, że wewnątrz klasy wykonane jest zapytanie HTTP.
  • Różnica między Fabryką a Fasadą wynika głównie z ccelu powstania klasy – Fabryka służy do ukrycia szczegółów konstrukcji wytwarzanych obiektów, zaś Fasada służy ukryciu szczegółów implementacyjnych zaimplementowanego mechanizmu i wystawieniu przyjaznego interfejsu.
  • Adapter różni się od Fasady tym, że założeniem Adaptera jest zapewnienie kompatybilności między dwoma niekompatybilnymi interfejsami.
  • Zdarza się tak, że Fasada jest jednocześnie Singletonem.

Podsumowanie

Mam nadzieję, że idea oraz cel stosowania wzorca projektowego Fasada jest już dla Ciebie zrozumiała! Serdecznie zachęcam do pozostawienia komentarza, szczególnie jeśli masz jakieś uwagi lub wątpliwości. Będzie mi również bardzo miło jeśli udostępnisz ten wpis aby trafił do większej liczby czytelników.

Źródła i materiały dodatkowe:

Czy wiesz, że devszczepaniak.pl jest na Facebooku?

Jeśli chcesz otrzymywać informację o nowym wpisie natychmiast po opublikowaniu wpisu to koniecznie polub devszczepaniak.pl na Facebooku.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.