Fasada (Facade)

Fasada

Fasada (Facade) jest strukturalnym wzorcem projektowym, który wyposaża bibliotekę, framework lub inny złożony zestaw klas w uproszczony interfejs.

Cel

Fasada zapewnia ujednolicony interfejs do zestawu interfejsów w podsystemie. Ten wzorzec definiuje interfejs wyższego poziomu, który ułatwia korzystanie z podsystemu.

Problem

Budujesz system, w którym chcesz wiedzieć, czy klient może otrzymać kredyt, czy ma zdolność kredytową.

Jednak obliczenie tego może być dość skomplikowane. Po pierwsze powiedzmy, że musimy wywołać serwis, który sprawdza, czy klient ma wystarczającą ilość oszczędności własnych. Następnie musimy wywołać inny serwis, który sprawdza, czy klient ma pożyczki. I na koniec musimy wywołać kolejny serwis, który sprawdza, czy klient ma inne kredyty. Może być również potrzeba wywołanie jakiś innych serwisów.
Te obliczenia nie mają więc ujawniać informacji i należy je wykonać w różnych częściach aplikacji.

Rozwiązanie

Umieszczenie przed nimi fasady ukrywa złożoność.

Jest to więc abstrakcja, która zachęca do ponownego wykorzystania i możemy nazwać ją MortgageFacade. Fasada zawiera jedną metodę, IsEligible.

Metoda ta jest odpowiedzialna za wywoływanie różnych usług i sprawdzenie zdolności kredytowej wnioskodawcy.

Przypiszmy to do struktury wzorca.

Struktura

Struktura jest dość prosta.

Przede wszystkim mamy fasadę, która jest odwzorowana w naszym przykładzie na MortgageFacade. Ta fasada ma wiedzę na temat klas podsystemów odpowiedzialnych za żądania i deleguje żądania do odpowiednich klas podsystemów.

Ten podsystem to wszystkie nasze inne usługi lub klasy. BankService, CreditService, LoanService i inne są podsystemami. W zależności od potrzeb realizują własną funkcjonalność i zajmują się pracą zleconą przez obiekt Fasady. Innymi słowy, metody wywoływane przez fasadę są zaimplementowane w podsystemie. Same podsystemy nie mają wiedzy o fasadzie. Zaimplementujmy teraz nasz przykład w kodzie.

Przykład implementacji

Pierwszą rzeczą, którą zrobimy, to jest utworzenie klas podsystemów. Nasza pierwsza klasa podsystemu to BankService.

/// <summary>
/// The Subsystem 1
/// </summary>
public class BankService
{
   // Czy ma wystarczające oszczędności
   public bool HasSufficientSavings(Customer customer)
   {
      Console.WriteLine("Sprawdz bank dla " + customer.Name);

      var savings = 50000;

      if (savings > customer.Amount / 3)
         return true;
      else
         return false;
   }
}

Serwis ten sprawdzi, czy klient ma wystarczające oszczędności, aby kwalifikować się do kredytu hipotecznego. Serwis ma jedną metodę, HasSufficientSavings (ma wystarczające oszczędności), która pobiera klienta. Dokonujemy tutaj testowych obliczeń tylko w celach demonstracyjnych.

Druga klasa podsystemu to CreditService, która służy do sprawdzenia kredytu dla klienta.

/// <summary>
/// The Subsystem 2
/// </summary>
public class CreditService
{
   public bool HasCredit(Customer customer) 
   {
      Console.WriteLine("Sprawdz kredyt dla " + customer.Name);
            
      return true;
   }
}

Te obliczenia mogą być oczywiście bardzo rozbudowane. My w celach demonstracyjnych zwracamy po prostu tylko true.

Ostatnią klasą podsystemu jest LoanService, która sprawdza pożyczki klienta.

/// <summary>
/// The Subsystem 3
/// </summary>
public class LoanService
{
   public bool HasLoans(Customer customer)
   {
      Console.WriteLine("Sprawdz pożyczki dla " + customer.Name);

      return true;
   }
}

Te obliczenia również mogą być oczywiście bardzo rozbudowane. My w celach demonstracyjnych zwracamy po prostu tylko true.

I oczywiście klasa naszego klienta, dla którego sprawdzamy zdolność kredytową.

/// <summary>
/// Customer class
/// </summary>
public class Customer
{
   private string name;
   private decimal amount;

   public Customer(string name, decimal amount)
   {
      this.name = name;
      this.amount = amount;
   }

   public string Name
   {
      get { return name; }
   }

   public decimal Amount
   {
      get { return amount; }
   }
}

Teraz przechodzimy do Fasady, którą nazwiemy MortgageFacade (kredyt hipoteczny fasada). To klasa, która powinna zawierać odniesienia do naszych podsystemów.

/// <summary>
/// The Facade class
/// Kredyt hipoteczny
/// </summary>
public class MortgageFacade
{
   BankService bankService = new BankService();
   LoanService loanService = new LoanService();
   CreditService creditService = new CreditService();

   // Czy kwalifikuje się
   public bool IsEligible(Customer customer)
   {
      Console.WriteLine("{0} ubiega się o {1:C} pożyczki\n", customer.Name, customer.Amount);
      bool eligible = true;

      // Sprawdź zdolność kredytową wnioskodawcy
      if (!bankService.HasSufficientSavings(customer))
      {
         eligible = false;
      }
      else if (!creditService.HasCredit(customer))
      {
         eligible = false;
      }
      else if (!loanService.HasLoans(customer))
      {
                eligible = false;
      }
         
      return eligible;
   }
}

Tak więc dodajemy trzy odniesienia do podsystemów. Moglibyśmy to wstrzyknąć, ale w naszym przypadku nie mamy skonfigurowanego kontenera IOC, więc po prostu je dodajemy. Moglibyśmy też mimo wszystko dodać konstruktor i pozwolić, aby klasa klienta dostarczyła nam instancje klas podsystemu.

Teraz musimy obliczyć czy wnioskodawca kwalifikuje się do kredytu hipotecznego. Dodajemy jedną metodę publiczną, IsEligible, która pobiera klienta. Najpierw wywołujemy bankService i sprawdzamy, czy klient ma wystarczające oszczędności, następnie creditService i sprawdzamy inne kredyty klienta i wywołujemy loanService i sprawdzamy pożyczki klienta. I na koniec metoda zwraca informację czy wnioskodawca kwalifikuje się do kredytu hipotecznego, czy nie.

I to wszystko w przypadku Fasady. Nasz wzór jest już gotowy. Przetestujmy to i przechodzimy do Program.cs

using Facade;
Console.Title = "Facade ";

Zaczynamy od utworzenia naszej fasady.

MortgageFacade mortgage = new MortgageFacade();

Następnie tworzymy klienta, który chce dostać kredyt.

Customer customer = new Customer("Adam Nowak", 100000);

A następnie testujemy naszą fasadę, próbując uzyskać kredyt hipoteczny dla naszego klienta.

bool eligible = mortgage.IsEligible(customer);

I wypisujemy wynik czy kredyt został przyznany, czy odrzucony.

Console.WriteLine("\nKredyt dla " + customer.Name +" został " + (eligible ? "Zatwierdzony" : "Odrzucony"));
Console.ReadKey();

Uruchommy teraz nasze rozwiązanie.

Wynik działania

Adam Nowak ubiega się o 100 000,00 zł pożyczki

Sprawdz bank dla Adam Nowak
Sprawdz kredyt dla Adam Nowak
Sprawdz pożyczki dla Adam Nowak

Kredyt dla Adam Nowak został Zatwierdzony

Zastosowanie

Pierwszym przypadkiem użycia tego wzorca jest oczywiście dostarczenie prostego interfejsu do złożonego podsystemu składającego się z kilku klas podsystemów. W takim przypadku, gdy podsystem staje się bardziej złożony, nie ma to wpływu na klientów korzystających z fasady.

Warto również wziąć pod uwagę ten wzorzec, gdy istnieje wiele zależności między klientem a klasami implementacji abstrakcji. Umieszczając fasadę pomiędzy nimi, oddzielasz podsystem od klientów.

Zalety i wady

Przede wszystkim Fasada chroni klientów przed podsystemami. Oznacza to zmniejszenie liczby obiektów, z którymi mają do czynienia klienci.

Promuje również słabe sprzężenie między podsystemem a jego klientami. Klient jest połączony z fasadą, a nie z podsystemami jako takimi. Dzięki temu słabemu połączeniu elementy podsystemu mogą się zmieniać bez wpływu na klienta. To zasada open/closed z zasad SOLID.

Fasada, mimo że chroni klientów przed podsystemami, klientom nie zabrania się używania klas podsystemów, jeśli chcą.

Kod źródłowy

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *