Warstwa Abstrakcji - stanowi izolację pomiędzy kodem programisty a zewnętrznymi modułami, pozwala odseparować system od jego zależności przez co czyni go podatnym na zmiany i aktualizacje. Przywiązując system do innej usługi czynimy go słabym.

Fasada

Typ: Strukturalny

Fasada ma na celu ujednolicenie i uproszczenie wielu, a czasami tylko jednego interfejsu. Prosto mówiąc, fasada agreguje elementy systemu tworzące większą całość udostępniając uproszczony interfejs pozwalający na skorzystanie z tej całości bez konieczności dokładnego poznawania każdego pojedynczego elementu.
Kolejną istotną często niedocenianą cechą fasady jest ukrycie nieistotnych z pewnej perspektywy części systemu co nie tylko upraszcza konkretną funkcjonalność a dodatkowo chroni części systemu, do których dostęp nie jest z poziomu tej funkcjonalności konieczny.

Przykładowa implementacja

<?php

class SubSystem1
{
    public function doSomething()
    {
    }
}

class SubSystem2
{
    public function doSomethingElse()
    {
    }
}

class SubSystem3
{
    public function doEpicStuff()
    {
    }

    public function doLessEpicButsStillImportantStuff()
    {
    }
}

class Facade
{
    private $subSystem1;
    private $subSystem2;
    private $subSystem3;

    public function __construct()
    {
        $this->subSystem1 = new SubSystem1();
        $this->subSystem2 = new SubSystem2();
        $this->subSystem3 = new SubSystem3();
    }

    public function doSomeComplexOperation()
    {
        $this->subSystem1->doSomething();
        $this->subSystem2->doSomethingElseWith();
        $this->subSystem3->doEpicStuff();
    }
}

Kiedy używać

Czytając Wikipedię można odnieść wrażenie, że wzorzec ten jest wręcz idealny podczas programowania systemu gdzie modelem jest komputer, posiadający system operacyjny, bios czy procesor. Niestety mało kto trafia na tak abstrakcyjne przypadki.

Kolejnym przykładem jest klasa agregująca kilka lub kilkanaście obiektów i udostępniająca metody z tych obiektów poprzez swoje statyczne metody. Przez takie przykłady panuje ogólne przekonanie, że Fasada powinna składać się jedynie z metod statycznych.

Fasady jednak sprawdzają się o wiele bardziej w przypadku kiedy jakaś operacja w systemie jest sekwencją innych operacji, które muszą wykonać się w określonej kolejności, oraz kiedy te operacje wykonywane są w kilku miejscach systemu.

Przykład użycia

Specyfikacja

  • należy utworzyć interfejs pozwalający manipulować obrazami
  • powinien istnieć interfejs odpowiedzialny za utworzenie nowego obrazu o określonym rozmiarze na podstawie obrazu źródłowego
  • powinien istnieć interfejs odpowiedzialny za obrócenie obrazu o podany kąt
  • powinien istnieć interfejs odpowiedzialny za zapisanie obrazu z uwzględnieniem określonej jakości
  • powinna istnieć możliwość transformacji obrazu źródłowego poprzez pomniejszenie go do rozmiaru 500x500px oraz zapisanie go w 50% oryginalnej jakości

Implementacja


namespace ImageTools; interface Manipulator { public function resize(Image $image, Size $newSize); public function rotate(Image $image, Angle $angle); public function changeQuality(Image $image, Percentage $quality); } namespace ImageManipulator\Manipulator; final class GDImageManipulator implements \ImageTools\Manipulator { private $gdWrapper; public function __construct(GDWrapper $gdWrapper) { $this->gdWrapper = $gdWrapper; } public function resize(Image $image, Size $newSize) { if ($newSize->isBiggerThan($this->gdWrapper()->getWidth($image), $this->gdWrapper()->getHeight($image)) { throw new InvalidImageSizeException("Original image size can't be increased."); } return $this->gdWrapper->createNewFrom($image, $newSize->width(), $newSize->height()); } public function rotate(Image $image, Angle $angle) { return this->gdWrapper->rotate($image, $angle->value()); } public function changeQuality(Image $image, Percentage $quality) { return $this->gdWrapper->save($image, $quality->value()); } } final class ImageManipulator { public function __construct() { $this->imageManipulator = new GDImageManipulator(new GDWrapper()); } public function resize($imagePath, $width, $height, $quality = 75) { $image = $this->imageManipulator->resize(new Image($imagePath), new Size($width, $height)); $this->imageManipulator->changeQuality($image, new Percentage($quality)); } } class Controller { private $imageManipulator; private $images; public function __construct(\ImageManipulator\Manipulator\ImageManipulator $imageManipulator, Images $images) { $this->imageManipulator = $imageManipulator; $this->images = $images; } public function mediumResizeAction($imageId) { $path = $this->images->getImagePathById($imageId); $this->imageManipulator->resize($path, 500, 500, 50); return new RedirectResponse('/'); } }

Omówienie

W powyższym przykładzie fasada nie tylko ułatwia wykonanie operacji zmiany rozmiaru obrazu, ukrywa również implementację interfejsu ImageManipulatorInterface. Dzięki takiemu podejściu developer nie musi być świadomy kolejności wykonywania poszczególnych metod, nie musi również rozumieć jak działa GDWrapper.

Błędy?

Znalazłeś błąd?
Chciałbyś podać lepszy przykład użycia?
A może nie zgadzasz się z implementacją przykładu?

Edytuj na Github.com Popraw lub zgłoś błąd

Pytania

Masz pytania odnośnie omawianego wzorca? Chciałbyś zapytać czy wzorzec pasuje do problemu, który aktualnie rozwiązujesz? Dobrze się składa, to jest idealne miejsce.

Błędy, sugestie ich poprawy, dyskusję na temat poprawności implementacji danego przykładu prosimy prowadzić poprzez Github Issues
Akceptuję

Ten serwis używa plików cookies. Więcej o plikach cookies.