Façade Wzór.21 Czasami chcemy współpracować z zaawansowanym, skompilowanym systemem w bardzo prosty sposób. Czasami chcemy się zabezpieczyć przed zmianami pochodzących od tego złożonego systemu. W Domain Driven Desing nawet na to się mówi "warstwę anty korupcyjną" (Anti-Corruption Layer). 

My jednak dzisiaj mówimy o wzorcu projektowym "Fasada", który istnieje i będzie istniał zawsze. 

Przykładem fizycznym może być twój dom, który ma skomplikowane połączenia elektryczne, ale ostatecznie Ciebie interesuje fakt, że jak wciśniesz guzik to zapali Ci się światło.

W prawdziwym życiu pisałem wiele fasad do skomplikowanych usług. Czasem rola fasady też polegała na tłumaczeniu jednego API pełnego polskich nazw na fasadę, która zwróci te dane w obiektach i polach po angielsku. Chociaż można się kłócić, że ten ostatni przykład podchodzi pod adapter.

Jaka jest różnica między adapterem a fasadą? Bo na pierwszy rzut oka oba wzorce projektowe wydają się bardzo podobne. Oto moja analogia.

  • Adapter : dodajesz nakładę tak, aby bolce kwadratowe stały się trójkątne i mogły być używane dalej dla klienta, który lubi bolce trójkątne.
  • Fasada : dodajesz guzik, który ukrywa masę kabli i podzespołów. Przykładowo pilot do telewizora jest fasadą, która ukrywa przed tobą wewnętrzne skomplikowane mechanizmy twojego telewizora, jak i skomplikowany interfejs.

Pisanie Fasad to poważna sprawa.

Nie chce, abyś śmiał, ale spędziłem kiedyś 2 lata pracy i opieki nad pewną fasadą między usługami. 

Fasada też występuje w wielu miejscach w .NET. Wysłanie zapytań HTTP, aby pobrać rezultat jako napis, może wyglądać tak:

using System.Net;

string url = "http://www.cezarywalenciuk.pl/robots.txt";
var request = WebRequest.Create(url);
request.Credentials = CredentialCache.DefaultCredentials;

var response = request.GetResponse();
var dataStream = response.GetResponseStream();
var reader = new StreamReader(dataStream);
string responseFromServer = reader.ReadToEnd();

Console.WriteLine(responseFromServer);
reader.Close();
response.Close();

Wygląda to, na sporo pracy. Bądźmy szczerzy bez MSDN nie napisałbyś takiego kodu. Na szczęście .NET oferuje Ci fasadę, która ukryje cały ten rytuał pisania kodu do tej jednej linijki.

string url1 = "http://www.cezarywalenciuk.pl/robots.txt";
var responseFromServer1 = new WebClient().DownloadString(url1);
Console.WriteLine(responseFromServer1);

W tym przypadku "WebClient" jest fasadą, którą oferuje przyjaźniejszy interfejs, który pozwala ci osiągnąć dokładnie to samo bez pisania całego rytuału.

Analogicznie możesz stworzyć pomocniczą metodę w swoim kodzie. W swojej karierze, jeśli jeszcze nie pisałeś to kiedyś na pewno stworzysz powłokę Fasady, która upraszcza komunikację między jedną usługą  a drugą. 

Pozostało Ci pokazać konkretny przykład użycia Fasady.

Tworzenie tabelki w konsoli

Moim celem jest stworzenie fasady, która narysuje mi tabelkę w konsoli. Chce ukryć użycie wszystkich Console.Writeline do mojej klasy.

Chce także określić liczbę spacji, od której będzie moja tabelka rysowana. Dla ułatwienia moja klasa będzie wyświetlać tabelkę tylko wartości i kluczy w danym słowniku.

public  class ConsoleTableFacade
{
    public char WriteSymbol { get; set; }
    public int NumberOfColums { get; set; }

    public Dictionary<string, string> Dictionary { get; set; }

    public int Intend { get; set; }

    public ConsoleTableFacade(Dictionary<string,string> dic, int intend, char writesymbol)
    {
        NumberOfColums = 2;
        WriteSymbol = writesymbol;
        Dictionary = dic;
        Intend = intend;
    }

Oto ilość kodu, którą trzeba napisać, aby stworzyć taką tabelkę bez żadnych paczek NuGet.

public void Write()
{
    int biggestKeySize = 0;
    int biggestValueSize = 0;

    foreach (var item in Dictionary)
    {
        if (biggestKeySize < item.Key.Length)
            biggestKeySize = item.Key.Length;

        if (biggestValueSize < item.Value.Length)
            biggestValueSize = item.Value.Length;
    }

    biggestKeySize++;
    biggestValueSize++;
    foreach (var item in Dictionary)
    {
        Console.WriteLine();
        Console.Write(new string(' ', Intend));
        Console.WriteLine(new string(WriteSymbol, biggestKeySize + biggestValueSize + 5));
        Console.Write(new string(' ', Intend));
        Console.Write(WriteSymbol);
        Console.Write(" "+item.Key);
        Console.Write(new string(' ', biggestKeySize - item.Key.Length));
        Console.Write(WriteSymbol);
        Console.Write(" "+item.Value);
        Console.Write(new string(' ', biggestValueSize - item.Value.Length));
        Console.Write(WriteSymbol);
    }

    Console.WriteLine();
    Console.Write(new string(' ', Intend));
    Console.WriteLine(new string(WriteSymbol, biggestKeySize + biggestValueSize + 5));
    Console.WriteLine();
}

Pozostało Ci pokazać jak moja fasada działa.

var dic  = new Dictionary<string, string>()
        {
            {"Afghanistan","Kabul" },
            {"Brazil","Brasilia" },
            {"Bulgaria","Sofia" },
            {"Bolivia","Sucre" },
            {"Cyprus","Nicosia" },
            {"Denmark","Copenhagen" },
            {"England","London" },
            {"Finland","Kabul" },
            {"France","Paris" },
            {"Poland","Warsaw" },
        };

var fasade = new ConsoleTableFacade(dic, 5, '+');
fasade.Write();

Jak widzisz uprościłem interfejs tworzenia takiej tabelki. Muszę tylko podać słownik, znak tworzący ciało mojej tabelki oraz przesunięcie boczne, od kiedy tabelka ma być rysowana.

Działanie mojej fasady

Moja fasada może teraz być użyta do innych słowników. Fasada jest tak popularnym wzorem projektowym, że być może stworzyłeś całą armię takich klas i metod, które upraszczają, jakieś istniejące API i nawet nie wiedziałeś, że jest to jakiś wzorzec projektowy.

Podsumowanie

Fasada to prosty wzorzec projektowy, którego celem jest uproszczenie istniejącej jakieś funkcjonalności tak, aby ktoś inny mógł z niej skorzystać bez bólów głowy.