Connect with us

Cześć, czego szukasz?

Internet Maker

Wzorce projektowe – fasada

Fasada jest wzorcem zaliczającym się do wzorców strukturalnych. Jest to jeden z popularniejszych, a jednocześnie najprostszych wzorców projektowych.

Wyobraź sobie sytuację, gdy masz złożony
system. W pewnym momencie musisz
zapewnić funkcjonalność, która możesz
osiągnąć poprzez złożenie kilku usług oferowanych
przez klasy wchodzące w skład systemu. Może to
wyglądać na przykład tak – naliczanie rabatu do
zakupów klienta:

$objKlient = new Klient($intIdKlienta);
$objKoszyk = new Koszyk($objKlient);
$objRabat = new Rabat();
$fltRabat = $objRabat->liczRabat($objKoszyk->getSumaKoszyka()+ $objKlient->getRabatIndywidualny());
Klient klient = new Klient(idKlienta);
Koszyk koszyk = new Koszyk(klient);
Rabat rabat = new Rabat();
Float Rabat = rabat.liczRabat(koszyk.getSumaKoszyka() + klient.getRabatIndywidualny());

Nie ma się czym przejmować, jeśli taka
konstrukcje mamy w jednym miejscu. Jeśli jednak
podobna konstrukcja będzie potrzebna w wielu
miejscach, warto zadbać o to, aby nie kopiować jej
wszędzie za pomocą kombinacji CTRL+C CRTL+V.
W przeciwnym razie w przypadku konieczności
zmodyfikowania powyższej formuły, staniemy
przed koniecznością odszukania poprawianego
fragmentu wszędzie tam gdzie go skopiowaliśmy
i poprawieniem go w tych wszystkich miejscach
z osobna. Tu z pomocą przychodzi fasada.

Przykładowa aplikacja

Tym razem ze względu na sposób działania
wzorca i opis problemu zamieszczony powyżej,
nasza aplikacja będzie od razu opierała się na prezentowanym
wzorcu. Będzie to klasa zarządzająca
przyznawaniem rabatów w sklepie internetowym.

PHP:
class FasadaRabatów {
private $objKlient;
private $objRabat;
private $objKoszyk;
private $objZamowienia;
public function_construct($intIdKlienta) {
    $this->objKlient = new Klient($intIdKlienta);
    $this->objRabat = new Rabat($this->objKlient);
    $this->objKoszyk = new Koszyk();
    $this->objZamowienia = new Zamowienia($this->objKlient);
}
public function rabatIndywidualny() {
return $this->objKlient->getRabatIndywidulany();
}
public function
rabatOdZakupowWKoszyku() {
     return $this->objRabat->liczRabat($this->objKoszyk->getWartosc());
}
public function
getRabatOdWielkosciZamowien() {
     return $this->objRabat->liczRabat($this->objZamowienia->getSume());
}
}
public class FasadaRabatow {
private Klient klient;
private Rabat rabaty;
private Koszyk koszyk;
private Zamowienia zamowienia;
public function FasadaRabatow(IntegeridKlienta) {
  klient = new Klient(idKlienta);
  rabaty = new Rabat(klient);
  koszyk = new Koszyk();
  zamowienia = new Zamowienia(klient);
}
public Float rabatIndywidualny() {
  return klient->getRabatIndywidulany();
}
public Float rabatOdZakupowWKoszyku() {
  return rabat->liczRabat(koszyk->getWartosc());
}
public Float
getRabatOdWielkosciZamowien() {
  return rabat->liczRabat(zamowienia->getSume());
}
}

Poniżej prezentujemy jeszcze szkielety klas
Klient, Rabat, Koszyk i Zamowienia. Jak zawsze jest
to jedynie szkielet, nie zawierający wartościowego
kodu, mający na celu jedynie zaprezentowanie
działania wzorca:

class Klient {
private $strImie;
private $strNazwisko;
private $fltRabat;
// ... inne dane o kliencie ...
public function __ construct($intIdKlienta) {
  //pobranie informacji o kliencie
}
public function getImie() {return $this->strImie;
}
public function getNazwisko() {
  return $this->strNazwisko;
}
public function getRabatIndywidualny()
{
  return $this->fltRabat;
}
}
class Rabat {
  private $objKlient;
  public function __construct(Klient $objKlient) {
  $this->objKlient = $objKlient;
}
public function liczRabat() {
  //liczenie rabatu
  return $fltRabat;
}
}
class Koszyk {
  public function getProdukty() {
  // wyciągniecie informacji o produktach w koszyku
  return $arrKoszyk;
}
public function dodajProdukt($intId) {
  // dodanie produktu do koszyka
}
public function usunProdukt($intId) {
  // usunięcie produktu z koszyka
}
public function getWartosc() {
  // obliczenie wartości produktów w koszyku
  return $fltWartosc;
}
}
class Zamowienia {
  private $objKlient;
  public function __construct(Klient $objKlient) {
  $this->objKlient = $objKlient;
}
public function zamow(Koszyk $objKoszyk) {
  // zamówienie produktów z koszyka
}
public function getSume() {
  //oblicz sumę wszystkich zrealizowanych zamówień
  return $fltSuma;
}
}
public class Klient {
private String imie;
private String nazwisko;
private Float rabat;
// ... inne dane o kliencie ...
public function Klient(Integer idKlienta) {
//pobranie informacji o kliencie
}
public String getImie() {
  return imie;
}
public String getNazwisko() {
  return nazwisko;
}
public Float getRabatIndywidualny() {
  return rabat;
}
}
public class Rabat {
  private Klient klient;
  public function __construct(Klient klient) {
   this.klient = klient;
}
public Float liczRabat() {
  //liczenie rabatu
  return rabat;
}
}
public class Koszyk {
  public ArrayList
  getProdukty() {
  // wyciągniecie informacji o produktach w koszyku
  return koszyk;
}
public void dodajProdukt(Integer id) {
  // dodanie produktu do koszyka
}
public void usunProdukt(Integer id) {
 // usunięcie produktu z koszyka
}
public Float getWartosc() {
  // obliczenie wartości produktów w koszyku
  return wartosc;
}
}
public class Zamowienia {
  private Klient klient;
  public function_construct(Klient klient) {
   this.klient = klient;
}
public void zamow(Koszyk koszyk) {
  // zamówienie produktów z koszyka
}
public Float getSume() {
  //oblicz sumę wszystkich zrealizowanych zamówień
  return suma;
}
}

Jak to działa? Program wszędzie tam, gdzie
potrzeba jest na przykład wartość rabatu od
sumy produktów w koszyku klienta, nie składa
za każdym razem kilku klas, a korzysta z jednej,
FasadaRabatów i z jej metody rabatOdZakupowWKoszyku(). Podsumowując, fasada posłużyła do
zbudowania często używanych metod z klocków
jakimi jest zbiór kilku klas systemu.

Poznałeś już kilka wzorców projektowych. Ich
liczba jest już wystarczająca aby analizować swoje
programy i zastanowić się gdzie i co mogłeś
zrobić lepiej. Warto też zastanowić się jak można
skutecznie połączyć klika wzorców projektowych
w jeden.

Kiedy używać i co nam to daje

Nad zastosowaniem fasady warto zastanowić się wówczas, gdy chcesz obudować złożony system
prostym interfejsem lub w systemie występuje wiele zależności pomiędzy klasami abstrakcyjnymi
a klasami je implementującymi.

Dzięki fasadzie można uniezależnić podsystem i zapewnić jego przenośność. A dokładniej, oddzielić
kod kliencki od komponentów, dostarczając mu jeden (lub klika, ale zawsze mniej niż istnieje komponentów)
interfejs obsługi podsystemu. Sprzyja to łatwiejszemu użyciu. Komponenty zostają o wiele
słabiej powiązane z kodem klienckim, dzięki czemu można je łatwiej podmienić w kodzie fasady, co nie
powinno mieć wpływu na działanie kodu klienckiego.

Pamiętaj jednak że fasada nie blokuje dostępu do komponentów, które obudowuje. W kodzie kliencki
możesz użyć fasady lub bezpośrednio komponentów – w zależności od potrzeby.

Fasada w pigułce

  • Wyodrębnij rodziny działań które w systemie
    często są wykonywane,
  • Działania z tej samej rodziny skomponuj
    w metody nowej klasy,
  • W metodach klasy fasady użyj do wykonania
    zaplanowanych działań innych obiektów
    systemu.

Może cię też zainteresować

Internet Maker

Nadszedł czas na kolejny prosty wzorzec projektowy: strategia (ang. strategy). W jakiej sytuacji może nam pomóc? Wyobraź sobie sklep internetowy i jego system liczenia marży. Warto dać...

Internet Maker

Wyobraź sobie sytuację, w której chciałbyś, aby w twojej aplikacji można było łatwo zmienić bazę danych, z której korzysta – np. z MySQL-a na PostgreSQL-a, a w razie potrzeby także na dowolną inną. Właśnie za to będzie odpowiadał...

Internet Maker

Dziś poznasz jeden z prostszych wzorców projektowych (strukturalnych). Wzorzec ten nosi nazwę proxy, co jest czasami tłumaczone jako pełnomocnik (takie tłumaczenie można znaleźć między innymi...

Internet Maker

Pisząc programy, często trzeba zbudować klasy, których obiekty mogą znajdować się w jednym z kilku stanów. Zadanie nie wydaje się trudne, często jednak modyfikacja kodu napisanego w typowy sposób...