Wzorce strukturalne: Adapter, Decorator, Facade
Proszę komponować obiekty i upraszczać interfejsy za pomocą wzorców strukturalnych.
Wzorce strukturalne: Adapter, Decorator, Facade to bezpłatna lekcja PHP Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej PHP Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs PHP Academy zawiera 4 lekcji w sumie.
Komponowanie obiektów
Wzorce strukturalne opisują, jak komponować klasy i obiekty w większe struktury, zachowując ich elastyczność. Omówimy trzy podstawowe wzorce:
- Adapter — sprawia, że niezgodny interfejs staje się użyteczny.
- Dekorator — dodaje zachowanie bez dziedziczenia.
- Fasada — udostępnia prosty interfejs złożonemu podsystemowi.
Adapter: problem
Kod zależy od interfejsu PaymentGateway, ale zewnętrzny SDK udostępnia metodę chargeCard($amountInCents) o innej postaci. Bezpośrednie wywoływanie SDK rozpowszechnia jego API w całym kodzie. Poniżej oba interfejsy są wyraźnie niezgodne — właśnie taką niezgodność rozwiązuje adapter.
<?php
// What our app wants to depend on
interface PaymentGateway { public function pay(float $dollars): string; }
// What the vendor actually gives us (cents, array result)
final class StripeSdk {
public function chargeCard(int $cents): array {
return ['id' => 'ch_1', 'amount' => $cents];
}
}
// dollars vs cents, string vs array -> shapes don't line up
var_dump((new StripeSdk())->chargeCard(2000));
Adapter w kodzie
Adapter przechowuje adaptowany obiekt i implementuje docelowy interfejs, konwertując argumenty oraz wartości zwracane. Kod aplikacji pozostaje przejrzysty i niezależny od dostawcy.
<?php
interface PaymentGateway { public function pay(float $dollars): string; }
// Third-party SDK with an incompatible API
final class StripeSdk {
public function chargeCard(int $cents): array {
return ['id' => 'ch_1', 'amount' => $cents];
}
}
final class StripeAdapter implements PaymentGateway {
public function __construct(private StripeSdk $sdk) {}
public function pay(float $dollars): string {
$res = $this->sdk->chargeCard((int) round($dollars * 100));
return "charged {$res['amount']}c -> {$res['id']}";
}
}
echo (new StripeAdapter(new StripeSdk()))->pay(20.0), PHP_EOL;
Dekorator: idea
Dekorator opakowuje obiekt, który udostępnia ten sam interfejs, i dodaje zachowanie przed delegowaniem lub po nim. W przeciwieństwie do dziedziczenia dekoratory można nakładać w czasie działania, co pozwala łączyć funkcje w dowolnej kolejności bez eksplozji liczby klas. Poniżej znajduje się minimalny szkielet: opakowanie implementujące ten sam interfejs i przechowujące wewnętrzną instancję.
<?php
interface Coffee { public function cost(): float; }
final class Espresso implements Coffee {
public function cost(): float { return 2.0; }
}
final class WithMilk implements Coffee {
public function __construct(private Coffee $inner) {}
public function cost(): float { return $this->inner->cost() + 0.5; }
}
echo (new WithMilk(new Espresso()))->cost(), PHP_EOL; // 2.5
Dekorator w kodzie
Każdy dekorator implementuje ten sam interfejs i przechowuje wewnętrzną instancję. W tym przykładzie źródło danych jest opakowane w pamięć podręczną i rejestrowanie, które można swobodnie komponować.
<?php
interface DataSource { public function read(): string; }
final class FileSource implements DataSource {
public function read(): string { return 'raw-data'; }
}
final class UpperCaseDecorator implements DataSource {
public function __construct(private DataSource $inner) {}
public function read(): string { return strtoupper($this->inner->read()); }
}
final class ExclaimDecorator implements DataSource {
public function __construct(private DataSource $inner) {}
public function read(): string { return $this->inner->read() . '!!!'; }
}
$src = new ExclaimDecorator(new UpperCaseDecorator(new FileSource()));
echo $src->read(), PHP_EOL; // RAW-DATA!!!
Dekoratory w praktyce
Middleware PSR-7 / PSR-15 to zasadniczo wzorzec dekoratora zastosowany do obsługi HTTP: każdy element middleware opakowuje następny, dodając wokół żądania i odpowiedzi uwierzytelnianie, rejestrowanie lub pamięć podręczną. Opakowania strumieni oraz warstwy pamięci podręcznej PSR-6/16 również często korzystają z dekoracji. Kluczowa cecha jest następująca: opakowanie można zastąpić obiektem, który ono opakowuje.
Dekorator a dziedziczenie
Dziedziczenie ustala zachowanie w czasie kompilacji i pozwala tylko na jedną klasę bazową. Dekoracja jest dynamiczna i kompozycyjna. Dekoratory należy wybierać, gdy:
- liczba kombinacji opcjonalnych funkcji rośnie,
- zachowanie powinno być dodawane lub usuwane w czasie działania,
- konieczne jest rozszerzenie klasy
finallub klasy zewnętrznej, po której nie można dziedziczyć.
Fasada: idea
Fasada udostępnia pojedynczy, uproszczony punkt wejścia do złożonego podsystemu składającego się z wielu klas. Nie ukrywa podsystemu (nadal można uzyskać do niego bezpośredni dostęp), ale oferuje wygodne API wysokiego poziomu dla typowych przypadków. Poniżej można porównać rozwlekłą sekwencję operacji podsystemu z jednoliniowym wywołaniem fasady na następnym ekranie.
<?php
// Without a facade, the client wires every step itself
final class Cpu { public function boot(): string { return 'cpu '; } }
final class Disk { public function load(): string { return 'disk '; } }
final class Ram { public function check(): string { return 'ram'; } }
$out = (new Cpu())->boot() . (new Disk())->load() . (new Ram())->check();
echo $out, PHP_EOL; // cpu disk ram
Fasada w kodzie
Fasada koordynuje kilku współpracowników za pomocą jednej metody. Klienci wywołują place() zamiast samodzielnie konfigurować obsługę magazynu, płatności i wysyłki.
<?php
final class Inventory { public function reserve(string $sku): bool { return true; } }
final class Payments { public function charge(float $amt): bool { return true; } }
final class Shipping { public function dispatch(string $sku): string { return 'tracking-99'; } }
final class OrderFacade {
public function __construct(
private Inventory $inv,
private Payments $pay,
private Shipping $ship
) {}
public function place(string $sku, float $amount): string {
if (!$this->inv->reserve($sku)) { return 'out of stock'; }
if (!$this->pay->charge($amount)) { return 'payment failed'; }
return 'shipped: ' . $this->ship->dispatch($sku);
}
}
echo (new OrderFacade(new Inventory(), new Payments(), new Shipping()))
->place('ABC', 49.99), PHP_EOL;
Rozróżnianie trzech wzorców
- Adapter zmienia interfejs, aby dwa istniejące elementy mogły się komunikować; zachowanie pozostaje takie samo, zmienia się tylko postać.
- Dekorator zachowuje interfejs, ale dodaje zachowanie, które można nakładać w czasie działania.
- Fasada wprowadza nowy, prostszy interfejs przed wieloma klasami.
Przydatna wskazówka: adapter dotyczy zgodności, dekorator — rozszerzania, a fasada — upraszczania.
Uwaga dotycząca fasad Laravela
„Fasady” Laravela są mechanizmem statycznego proxy wspieranym przez kontener usług, a nie wzorcem Fasady GoF. Łączy je cel w postaci wygodnego interfejsu, ale różnią się implementacją. Nie należy utożsamiać terminologii frameworka ze wzorcem strukturalnym podczas analizowania projektu.
Szybki test
Należy rozpoznać wzorzec na podstawie jego przeznaczenia.
Podsumowanie
Komponowano obiekty za pomocą trzech wzorców strukturalnych: Adaptera do łączenia niezgodnych interfejsów (np. zewnętrznego SDK), Dekoratora do nakładania zachowania w czasie działania (podstawa middleware PSR-15) oraz Fasady do udostępniania prostego interfejsu złożonemu podsystemowi. Każdy z nich zmniejsza powiązania, dzięki czemu kod jest odporniejszy na uzależnienie od dostawcy i rozrost funkcji.
Często zadawane pytania
Czy lekcja „Wzorce strukturalne: Adapter, Decorator, Facade” jest bezpłatna?
Tak — pełny tekst „Wzorce strukturalne: Adapter, Decorator, Facade” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu PHP Academy, przejdź na CoddyKit PRO. Kurs PHP Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Wzorce strukturalne: Adapter, Decorator, Facade”?
Proszę komponować obiekty i upraszczać interfejsy za pomocą wzorców strukturalnych. Ćwiczysz PHP Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć PHP Academy?
Nie wymagamy żadnego doświadczenia. PHP Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.
Ile czasu zajmuje lekcja „Wzorce strukturalne: Adapter, Decorator, Facade”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji PHP Academy?
Tak. Każda lekcja PHP Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Zasady SOLID w praktyce
- Wzorce kreacyjne: Factory, Builder, Singleton
- Wzorce strukturalne: Adapter, Decorator, Facade
- Wzorce behawioralne: Strategy, Observer, Command