Wzorzec projektowy Fasada

Nadszedł czas na kolejny wzorzec projektowy – pierwszym jaki poznaliśmy był Singleton, tym razem kolej na fasadę. Jeśli chcesz sobię przypomnieć czym są wzorce projektowe to warto wrócić do tej lekcji z czwartego tygodnia kursu. A teraz rozgryźmy fasadę. 😉

Skoro mamy do czynienia ze wzorcem projektowym to na początku wyjaśnimy sobie jaki problem rozwiązuje fasada, następnie przyjrzymy się jej implementacji, a na koniec zdefiniujemy sobie zadanie domowe polegające na implementacji fasady.

Jaki problem rozwiązuje fasada?

Z czym kojarzy Ci się fasada?

Mi się kojarzy z frontem jakiegoś wysokiego budynku – i takim tropem warto pójść.

Jeśli coś jest wysokie (i dosyć szerokie) to bez problemu może coś zakryć.

Słowo kluczowe – zakryć.

Po tych kilku skojarzeniach możemy wywnioskować co fasada nam daje – daje nam możliwość przysłonięcia – czego? Kodu – a dokładniej pewnej części implementacji.

Fasadę w takim razie możemy rozumieć jako taki interfejs, który jest wystawiony dla użytkownika, lecz użytkownik nie wie co się dzieje za fasadą. I to jest najważniejsze – nie wie co się dzieje. Podsumujmy to wszystko.

To czym jest ta fasada?

W fasadzie są stworzone metody, które zazwyczaj korzystają z metod serwisów, w jednym serwisie może być naprawdę sporo metod – a takich serwisów może być kilka.

W tym celu korzysta się z fasad, które ukrywają niepotrzebne fragmenty kodu serwisu/serwisów. Nic Ci jeszcze to nie mówi? Spójrzmy na klasyczny przykład – system bankowy.

Przypuśćmy, że mamy system bankowym – pierdyliard funkcji m.in.:

  • wpłaty
  • wypłaty
  • debet
  • lokaty
  • przelew zwykły
  • przelew natychmiastowy

I jeszcze tysiące innych funkcji – teraz założmy, że musimy stworzyć system dla bankomatu. Nie będziemy przecież pisać na nowo kodu, lepiej będzie napisać fasadę, która będzie zawierała tylko te fragmenty serwisów, które są potrzebne bankomatowi.

Jakie to mogą być?

  • Walidacja karty bankomatowej i PINu
  • Wypłata gotówki
  • Drukowanie potwierdzenia
  • Drukowanie wyciągu z konta

I są to funkcje, które w zupełności wystarczą naszemu bankomatowi – przecież nie są nam potrzebne funkcje odpowiedzialne np. za robienie przelewów.

Co nam to daje?

W sumie ukrywamy coś, ale co nam tak naprawdę to daje?

W przypadku rozrastania się aplikacji fasady pozwalają zachować większy porządek w kodzie, pozwalają w prostszy sposób zapanować nad aplikacją.

Dla każdego modułu mamy wystawioną jakąś fasadę i wiemy, że możemy korzystać tylko już z niej, a nie innych serwisów.

Tworząc interfejs bankomatu przez przypadek nie pomylimy jakieś funkcji lub niepotrzebnie wykorzystamy funkcję z X serwisu.

Implementacja fasady – bankomat

Skoro czołowym przykładem dla fasady jest interfejs bankomatu to stwórzmy sobie teraz jakiś przykładowy – składający się z dwóch prymitywnych serwisów. Chcę tylko Ci zobrazować jak zaimplementować fasadę. 😉

Na początku będziemy potrzebowali dwóch prostych serwisów – podkreślam prostych, ponieważ będą one zwracały z góry ustalone wartości lub wyświetlały tylko tekst w konsoli. Nigdy nie pisz tak swojego kodu, stworzyłem go aby w prosty sposób zobrazować Ci wzorzec projektowy fasadę. 😉

AccountService

Serwis jest odpowiedzialny za wypłacanie pieniędzy z konta, wpłacanie na nie oraz zwracanie stanu konta. Tyle nam wystarczy.

package pl.maniaq.service;

public class AccountService {

    public AccountService() {

    }

    public void withdraw(String cardNumber, Integer amount) {
        System.out.println("Z karty o numerze: " + cardNumber + " wypłacono: " + amount);
    }

    public void deposit(String cardNumber, Integer amount) {
        System.out.println("Na kartę o numerze: " + cardNumber + " wpłacono: " + amount);
    }


    public Integer balance(String cardNumber) {
        return 153;
    }
}

Jak widzisz są to naprawdę proste metody – podkreślam, że chodzi mi tylko tutaj o pokazanie Ci idei fasady, a nie implementancji sporej aplikacji, nad którą będziesz musiał się skupić zamiast nad samą fasadą.

CardService

Jeszcze serwis odpowiedzialny za obsługę kart:

package pl.maniaq.service;

public class CardService {

    public CardService() {

    }

    public void isCardCorrect(String cardNumber) {
        System.out.println("Sprawdzanie czy karta o numerze " + cardNumber + " jest poprawna.");
    }

    public void isDebitCard(String cardNumber) {
        System.out.println("Sprawdzanie czy karta o numerze " + cardNumber + " jest kartą debetową.");
    }

    public void isCreditCard(String cardNumber) {
        System.out.println("Sprawdzanie czy karta o numerze " + cardNumber + " jest kartą kredytową.");
    }
}

Serwis nam sprawdza czy karta jest debetowa, kredytowa, albo czy w ogóle jest poprawna.

Fasada

Mając dwa powyższe serwisy stworzymy fasadę – czyli interfejs bankomatu. Skorzystamy w nim tylko z tych metod, które są konieczne do instnienia bankomatu. 😉

Na początku stwórzmy nowy pakiet o nazwie facade – tam umieścimy naszą fasadę. Niech ma nazwę ATMFacade.

public class ATMFacade {

    public ATMFacade() {
    }

}

Dodajmy do fasady dwa pola – czyli dwa nasze serwisy.

public class ATMFacade {

    private AccountService accountService;
    private CardService cardService;

    public ATMFacade() {
        accountService = new AccountService();
        cardService = new CardService();
    }
}

I teraz stwórzmy interfejs naszego bankomatu, co może on mieć?

Wypłata gotówki

public void withdrawMoney(String cardNumber, Integer amount) {
      accountService.withdraw(cardNumber, amount);
  }

Aby wypłacić gotówkę korzystam z metody withdraw serwisu AccountService.

Sprawdzenie karty

Warto po włożeniu karty do bankomatu sprawdzić czy jest to karta poprawna:

public boolean isCardValid(String cardNumber) {
    cardService.isCardCorrect(cardNumber);
    return true;
}

Oczywiście u nas wygląda to bardzo skromnie, choć w rzeczywistości sprawdzanie wygląda dużo bardziej skomplikowanie. 😉

Sprawdzenie stanu konta

Niech nasz bankomat będzie jeszcze potrafił wyświetlić stan konta:

public Integer getAccountBalance(String cardNumber) {
    return accountService.balance(cardNumber);
}

 

I nasza cała fasada – interfejs bankomatu jest gotowy, czas go chyba użyć, nie uważasz?

Użycie fasady

Fasady użyjemy oczywiście w funkcji main – u mnie mieści się ona w klasie ATM – tak jak przystało na bankomat. 😉

W tej klasie zdefiniowałem sobie prostą metodę do wyświetlania jeszcze prostszego menu:

public static void displayMenu() {
    System.out.println("BANKOMAT");
    System.out.println("1 - sprawdź stań konta");
    System.out.println("2 - wypłać gotówkę");
    System.out.println("0 - zakończ");
}

W metodzie main stworzyłem obiekt naszej fasady:

public static void main(String[] args) {
    ATMFacade atm = new ATMFacade();
}

Dodałem jeszcze skaner do odczytu klawiatury:

Scanner scanner = new Scanner(System.in);

Jakiś stały numer karty:

String cardNumber = "1239505430239123";

Oraz zmienną sterującą naszym programem:

int data = -1;

Mając wszystkie potrzebne zmienne możemy ruszać do użycia fasady.

Na początku będziemy musieli sprawdzić czy podana karta jest prawidłowa – jeśli tak to bankomat może udostępnić nam resztę funkcji:

if (atm.isCardValid(cardNumber)) {
}

Jeśli przejdziemy weryfikację to czas na kluczowe działanie programu – czyli nieskończona pętla while:

if (atm.isCardValid(cardNumber)) {
    while (data != 0) {
        displayMenu();
        data = scanner.nextInt();
    }
}

I teraz musimy dodać instrukcję sterująca switch, aby “obsłużyć” to co napisaliśmy w menu.

Sprawdzenie stanu konta

switch (data) {
     case 1:
         Integer balance = atm.getAccountBalance(cardNumber);
         System.out.println("Stan konta o numerze " + cardNumber + ": " + balance + "zł.");
         break;

Przy użyciu fasady wyciągnęliśmy stan konta, a następnie go wyświetliliśmy – nic skomplikowanego.

Wypłata gotówki

case 2:
    System.out.println("Podaj kwotę jaką wypłacić: ");
    Integer amount = scanner.nextInt();
    atm.withdrawMoney(cardNumber, amount);
    break;

Na początku prosimy o podanie kwoty do wypłaty, a następnie używamy metody z fasady do wypłaty gotówki.

Exit

case 0:
    data = 0;
    System.out.println("Dziękujemy za skorzystanie z naszych usług.");
    break;

No i jeszcze case odpowiedzialny za zamknięcie bankomatu.

 

I cały nasz main wygląda tak:

package pl.maniaq;

import pl.maniaq.facade.ATMFacade;

import java.util.Scanner;

public class ATM {

    public static void displayMenu() {
        System.out.println("BANKOMAT");
        System.out.println("1 - sprawdź stań konta");
        System.out.println("2 - wypłać gotówkę");
        System.out.println("0 - zakończ");
    }

    public static void main(String[] args) {
        ATMFacade atm = new ATMFacade();
        Scanner scanner = new Scanner(System.in);
        String cardNumber = "1239505430239123";
        int data = -1;

        if (atm.isCardValid(cardNumber)) {
            while (data != 0) {
                displayMenu();
                data = scanner.nextInt();

                switch (data) {
                    case 1:
                        Integer balance = atm.getAccountBalance(cardNumber);
                        System.out.println("Stan konta o numerze " + cardNumber + ": " + balance + "zł.");
                        break;

                    case 2:
                        System.out.println("Podaj kwotę jaką wypłacić: ");
                        Integer amount = scanner.nextInt();
                        atm.withdrawMoney(cardNumber, amount);
                        break;

                    case 0:
                        data = 0;
                        System.out.println("Dziękujemy za skorzystanie z naszych usług.");
                        break;
                }
            }
        }


    }
}

Po uruchomieniu możemy skorzystać z bankomatu, sprawdzić stan konta i wypłacić gotówkę. Oczywiście jest to bardzo zasymulowane, jednak użyliśmy poprawnie naszej fasady.

Dobra, dobra, ale tak naprawdę po co?

Na początku wspominałem, że fasada ukrywa niepotrzebne fragmenty kodu, a wystawia tylko potrzebny interfejs – czyli zbiór metod, potrzebnych do danego przypadku.

Naszym przypadkiem był bankomat – mieliśmy łącznie 6 metod w serwisach, a ostatecznie wykorzystaliśmy tylko 3 z nich.

Czy warto było zastosować fasadę?

Moim zdaniem – tak!

Tworząc taki interfejs dużo prościej jest się odnaleźć podczas używania go – czyli w metodzie main.

Równie dobrze mógłbym nie tworzyć fasady i bezpośrednio w metodzie main wykorzystywać metody z serwisu, ale pytanie po co utrudniać sobie życie?

Dzięki fasadzie w klasie ATM otrzymałem gotowy interfejs, którego całkowicie, podkreślam – całkowicie – mogę wykorzystać. W przypadku serwisu, musiałbym zastanawiać się, które metody są mi potrzebne, a które nie.

Podsumowanie

Fasada jest prostym wzorcem projektowym, jest to tak naprawdę kolejny plik, w którym tworzymy konkretny interfejs z puli już istniejących metod – metod może być przecież nawet tysiące!

Dalsze pisanie aplikacji i używanie stworzonych wcześniej interfejsów jest prostsze i dużo bezpieczniejsze, aniżeli mielibyśmy korzystać bezpośrednio z serwisów.

Bezpieczeństwo jest ważne w takich właśnie instytucjach jak np. bankowość – dzięki fasadzie, kod aplikacji będzie dużo czytelniejszy i dzięki temu przez przypadek nie wywołamy – nie damy dostępu – do nieporządanej funkcji w konkretnym interfejsie.

Kod napisanej aplikacji w czasie lekcji możesz znaleźć w tym repozytorium.

Na utrwalenie wiedzy z fasad przyjdzie czas na koniec tygodnia, gdzie to jednym z zadań będzie właśnie implementacja fasady w aplikacji Management.

To już wszystko, do zobaczenia w kolejnej lekcji. 😉

 

Kamil Klimek

Od 2016 jestem programistą Java. Przez pierwsze 4 lata pracowałem jako Full Stack Java Developer. Później postanowiłem postawić nacisk na Javę, żeby jeszcze lepiej ją poznać.

Subscribe
Powiadom o
guest
2 komentarzy
najstarszy
najnowszy oceniany
Inline Feedbacks
View all comments
Klimek
Klimek
2 lat temu

Mam pytanie co do klasy AccountService, czy żeby fasada miała sens metody tej klasy nie powinny mieć ograniczonego dostępu(np protected albo bez modyfikatora) gdzie tylko nasz ATMFacade będzie miał możliwość komunikacji z nimi?

Piotrek L.
Piotrek L.
1 rok temu

Rewelka, bardzo przystępnie podane 🙂 Dziękuję

2
0
Would love your thoughts, please comment.x