Kurs PHP: Wzorce Projektowe w Praktyce

Wprowadzenie do wzorców projektowych w PHP

Wzorce projektowe w PHP to szablony, które pomagają w tworzeniu zgodnych z najlepszymi praktykami aplikacji. Pozwalają one na stworzenie skalowalnych i łatwych do utrzymania systemów informatycznych. Wzorce projektowe są szczególnie przydatne w programowaniu obiektowym, ponieważ umożliwiają wykorzystanie doświadczenia i wiedzy innych programistów. Dzięki wzorcom projektowym, programiści PHP mogą unikać typowych błędów, przyspieszać proces tworzenia aplikacji oraz zapewniać, że kod będzie bardziej czytelny i zrozumiały dla innych członków zespołu.

Wzorce projektowe, takie jak Singleton, Fabryka Abstrakcyjna czy Dekorator, są fundamentem dla tworzenia solidnych i elastycznych aplikacji. Każdy z tych wzorców rozwiązuje konkretne problemy projektowe, co pozwala na bardziej efektywne zarządzanie kodem i jego przyszłą rozbudowę. Wprowadzenie do wzorców projektowych to pierwszy krok w kierunku pisania profesjonalnego kodu w języku PHP. Wszystkie przykłady przedstawione poniżej są zgodne z najnowszymi wersjami PHP i pochodzą z praktycznego kursu PHP 8.4 w postaci ebooka.

Czym są wzorce projektowe?

Wzorce projektowe to sprawdzone rozwiązania typowych problemów programistycznych, które pozwalają na szybsze, efektywniejsze i bardziej czytelne tworzenie aplikacji. Są jak drogowskazy, które pomagają programistom pisać kod nie tylko działający, ale przede wszystkim czytelny, łatwy do utrzymania i rozbudowy. Znajomość wzorców projektowych to kluczowa kompetencja, której wymaga dzisiejszy rynek pracy od każdego programisty PHP.

W dobie dynamicznego rozwoju PHP – szczególnie w obliczu najnowszej wersji PHP 8.4 – umiejętność efektywnego stosowania wzorców projektowych jest cenniejsza niż kiedykolwiek wcześniej. Wykorzystanie tych wzorców pozwala skrócić czas pisania kodu, uniknąć częstych błędów oraz tworzyć aplikacje zgodne z najlepszymi praktykami branżowymi. Jeśli marzysz o pisaniu profesjonalnego, dobrze zorganizowanego i przyszłościowego kodu PHP, wzorce projektowe powinny znaleźć się na Twojej l iście priorytetów.

W moim nowym ebooku poświęconym PHP 8.4 krok po kroku wprowadzam Cię w świat najważniejszych wzorców projektowych, pokazując ich praktyczne zastosowanie w realnych projektach. Dzięki licznym przykładom szybko opanujesz nie tylko teorię, ale również zdobędziesz praktyczne doświadczenie, które przyda Ci się od razu w pracy zawodowej. Sposób przekazywania wiedzy podczas szkoleń, z naciskiem na praktyczne ćwiczenia, umożliwia lepsze zrozumienie i przyswojenie materiału. Zainwestuj w swój rozwój i już dziś sięgnij po ebooka, który otworzy przed Tobą drzwi do świata profesjonalnego PHP!

Wzorzec projektowy Fasada w PHP 8.4

Wzorzec projektowy Fasada (Facade) umożliwia uproszczenie interfejsu skomplikowanego systemu, udostępniając jedną spójną klasę do wykonywania różnych operacji. Klasa QueryBuilder z 8. rozdziału ebooka „DBAL - query builder” doskonale obrazuje ten wzorzec. Dzięki zastosowaniu fasady programiści mogą w łatwy sposób generować różne rodzaje zapytań do bazy danych – bez konieczności zagłębiania się w szczegóły implementacji poszczególnych klas odpowiedzialnych za budowanie konkretnych zapytań.

W omawianym przykładzie klasa QueryBuilder dostarcza wygodne metody, takie jak insert(), update(), delete() oraz select(), które odpowiadają za tworzenie i przygotowywanie konkretnych typów zapytań SQL. Każda z tych metod wewnętrznie ukrywa szczegóły pozyskiwania właściwych obiektów budujących zapytania z kontenera aplikacji (Application::getInstance()->get()), ich oczyszczania oraz konfiguracji. W ten sposób fasada znacząco zmniejsza złożoność kodu, z którą musiałby się mierzyć użytkownik, bez jej zastosowania

<?php

declare(strict_types=1);

namespace DJWeb\Framework\DBAL\Query\Builders;

use DJWeb\Framework\Base\Application;
use DJWeb\Framework\DBAL\Contracts\Query\DeleteQueryBuilderContract;
use DJWeb\Framework\DBAL\Contracts\Query\InsertQueryBuilderContract;
use DJWeb\Framework\DBAL\Contracts\Query\QueryBuilderFacadeContract;
use DJWeb\Framework\DBAL\Contracts\Query\SelectQueryBuilderContract;
use DJWeb\Framework\DBAL\Contracts\Query\UpdateQueryBuilderContract;

class QueryBuilder implements QueryBuilderFacadeContract
{
    public function insert(string $table): InsertQueryBuilderContract
    {
        /** @var InsertQueryBuilderContract $item */
        $item = Application::getInstance()->get(InsertQueryBuilderContract::class);
        $item->clean();
        return $item->table($table);
    }

    public function update(string $table): UpdateQueryBuilderContract
    {
        /** @var UpdateQueryBuilderContract $item */
        $item = Application::getInstance()->get(UpdateQueryBuilderContract::class);
        $item->clean();
        return $item->table($table);
    }

    public function delete(string $table): DeleteQueryBuilderContract
    {
        /** @var DeleteQueryBuilderContract $item */
        $item = Application::getInstance()->get(DeleteQueryBuilderContract::class);
        $item->clean();
        return $item->table($table);
    }

    public function select(string $table): SelectQueryBuilderContract
    {
        /** @var SelectQueryBuilderContract $item */
        $item = Application::getInstance()->get(SelectQueryBuilderContract::class);
        $item->clean();
        return $item->table($table);
    }
}

Warto zauważyć, że w przedstawionym przykładzie klasy QueryBuilder z rozdziału 8 ebooka „DBAL - query builder” wykorzystano dobrą praktykę polegającą na zwracaniu interfejsów (InsertQueryBuilderContract, UpdateQueryBuilderContract itd.), a nie konkretnych klas. Dzięki temu implementacja fasady zachowuje wysoki poziom abstrakcji i nie narzuca użytkownikowi sztywnych powiązań z konkretnymi klasami. Klient klasy fasady może programować do interfejsów, co zwiększa elastyczność oraz ułatwia testowanie aplikacji.

Wzorzec projektowy Fabryka w PHP 8.4

Wzorzec projektowy Fabryka (Factory) służy do tworzenia obiektów, których inicjalizacja może być skomplikowana lub zależna od wielu parametrów konfiguracyjnych. Świetnym przykładem zastosowania tego wzorca jest klasa MailerFactory z rozdziału 20 ebooka „Wysyłka maili”, która udostępnia statyczne metody, takie jak createSmtpMailer() czy createMailHogMailer(). Dzięki temu programista nie musi ręcznie budować obiektu Mailer wraz z odpowiednim ciągiem DSN, lecz korzysta z gotowych, jasno nazwanych metod fabrycznych.

Zastosowanie fabryki zapewnia, że obiekty klasy Mailer są zawsze tworzone poprawnie, z odpowiednimi ustawieniami. To znacznie upraszcza proces integracji aplikacji z różnymi serwerami pocztowymi, np. SMTP czy MailHog. Dzięki fabryce kod staje się bardziej czytelny, mniej podatny na błędy i łatwiejszy w utrzymaniu – zmiana konfiguracji wysyłki maili sprowadza się do użycia innej metody fabrycznej, a nie ingerowania w główną logikę aplikacji.

<?php

declare(strict_types=1);

namespace DJWeb\Framework\Mail;

final class MailerFactory
{
    public static function createSmtpMailer(
        string $host,
        int $port,
        string $username,
        string $password
    ): MailerContract {
        $dsn = sprintf(
            'smtp://%s:%s@%s:%d',
            $username,
            $password,
            $host,
            $port
        );
        return new Mailer($dsn);
    }

    public static function createMailHogMailer(
        string $host = 'localhost',
        int $port = 1025
    ): MailerContract {
        $dsn = sprintf('smtp://%s:%d', $host, $port);
        return new Mailer($dsn);
    }
}

Wzorzec projektowy Singleton w PHP 8.4

Wzorzec Singleton to podejście projektowe, które zapewnia istnienie tylko jednej instancji klasy w całym cyklu życia aplikacji, przy jednoczesnym dostarczeniu globalnego punktu dostępu do tej instancji. Klasa Application z rozdziału 3 ebooka, która implementuje kontener zależności (Dependency Injection), stanowi świetny przykład wykorzystania tego wzorca. Metoda statyczna getInstance() zapewnia dostęp do pojedynczej instancji aplikacji, co gwarantuje, że każdy komponent korzysta z tej samej konfiguracji i zasobów. Na tym kursie, programiści PHP zdobywają umiejętności w zakresie obiektowego programowania oraz aplikacji

W kolejnych rozdziałach ebooka (np. rozdział 5), klasa Application została rozbudowana o kolejne istotne elementy, jak np. obsługa konfiguracji za pomocą właściwości config. Zastosowanie nowości z PHP 8.4, takich jak właściwości typu getter, operator przypisania null coalescing (??=) oraz jawne deklarowanie typów w bardziej zaawansowanych konstrukcjach, znacząco zwiększyło czytelność i ergonomię kodu. Właściwość config demonstruje, jak dzięki nowoczesnym funkcjom języka, singleton może być elegancko połączony z wzorcem leniwej inicjalizacji (lazy loading), zapewniając maksymalną wydajność aplikacji.

Dodatkowo w klasie Application zastosowano zabezpieczenia, które są integralną częścią prawidłowego użycia wzorca Singleton, takie jak blokada klonowania (__clone) oraz serializacji (__serialize, __unserialize). Dzięki temu mamy pewność, że instancja klasy pozostanie unikalna przez cały cykl życia aplikacji. Połączenie singletona z kontenerem zależności w przykładzie klasy Application to efektywne podejście, które znakomicie ilustruje, jak w praktyce można implementować zaawansowane wzorce projektowe, wykorzystując najnowsze możliwości PHP 8.4.

<?php

declare(strict_types=1);

namespace DJWeb\Framework\Base;

use DJWeb\Framework\Config\ConfigBase;
use DJWeb\Framework\Config\Contracts\ConfigContract;
use DJWeb\Framework\Container\Container;
use DJWeb\Framework\Container\Contracts\ContainerContract;
use DJWeb\Framework\Container\Contracts\ServiceProviderContract;
use DJWeb\Framework\DBAL\Contracts\Schema\SchemaContract;
use DJWeb\Framework\Events\EventManager;
use DJWeb\Framework\Exceptions\Container\ContainerError;
use DJWeb\Framework\Log\LoggerFactory;
use DJWeb\Framework\ServiceProviders\SchemaServiceProvider;
use Psr\Log\LoggerInterface;

class Application extends Container
{
    public string $base_path{
        get => $this->getBinding('base_path') ?? '';
    }

    public ?ConfigContract $config{
        get {
            $this->config ??= $this->get(ConfigContract::class);
            $this->config->loadConfig();
            return $this->config;
        }
    }

    protected static ?self $instance = null;
    protected function __construct()
    {
        parent::__construct();
        $this->set(Container::class, $this);
        $this->set(ContainerContract::class, $this);
        $this->set(ConfigContract::class, new ConfigBase($this));
    }

    public function __clone()
    {
        throw new ContainerError('Cannot clone Application');
    }

    public function __serialize(): array
    {
        return [];
    }

    /**
     * @param array<string, mixed> $data
     *
     * @return void
     */
    public function __unserialize(array $data): void
    {
        json_encode($data, JSON_THROW_ON_ERROR);
        throw new ContainerError('Cannot unserialize Application');
    }

    public static function getInstance(): static
    {
        if (self::$instance === null) {
            /** @phpstan-ignore-next-line instance */
            self::$instance = new static();
        }
        /** @phpstan-ignore-next-line instance */
        return self::$instance;
    }

    public static function withInstance(?self $instance): void
    {
        self::$instance = $instance;
    }
}

Wzorzec projektowy Dekorator w PHP 8.4

Wzorzec projektowy Dekorator (Decorator) pozwala dynamicznie rozszerzać funkcjonalność obiektu, nie modyfikując jednocześnie jego oryginalnej struktury ani nie tworząc skomplikowanej hierarchii dziedziczenia. Dzięki temu można łatwo dodawać nowe funkcje, walidacje lub zachowania, nie naruszając zasady pojedynczej odpowiedzialności (Single Responsibility Principle). Klasy przedstawione w rozdziale 2 ebooka, implementujące standardy PSR-7 i PSR-17, doskonale obrazują, jak skutecznie używać tego wzorca w praktyce, zwłaszcza w kontekście obsługi plików przesłanych przez użytkownika. Uczestnicy kursu zdobędą umiejętności związane z zaawansowanymi technikami optymalizacji kodu oraz projektowaniem aplikacji zgodnych z najlepszymi praktykami.

<?php

declare(strict_types=1);

namespace DJWeb\Framework\Http\UploadedFile;

use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UploadedFileInterface;

abstract class UploadedFileDecorator implements UploadedFileInterface
{
    public function __construct(protected UploadedFileInterface $file)
    {
    }

    public function getStream(): StreamInterface
    {
        return $this->file->getStream();
    }

    public function moveTo(string $targetPath): void
    {
        $this->file->moveTo($targetPath);
    }

    public function getSize(): ?int
    {
        return $this->file->getSize();
    }

    public function getError(): int
    {
        return $this->file->getError();
    }

    public function getClientFilename(): ?string
    {
        return $this->file->getClientFilename();
    }

    public function getClientMediaType(): ?string
    {
        return $this->file->getClientMediaType();
    }
}
<?php

declare(strict_types=1);

namespace DJWeb\Framework\Http\UploadedFile;

use InvalidArgumentException;
use RuntimeException;

class PathValidatorDecorator extends UploadedFileDecorator
{
    public function moveTo(string $targetPath): void
    {
        $this->validatePath($targetPath);
        parent::moveTo($targetPath);
    }

    private function validatePath(string $targetPath): void
    {
        if (! $targetPath) {
            throw new InvalidArgumentException('Invalid path provided for move operation');
        }

        $targetDirectory = dirname($targetPath);
        if (! is_dir($targetDirectory) || ! is_writable($targetDirectory)) {
            throw new RuntimeException(
                sprintf('The target directory `%s` does not exist or is not writable', $targetDirectory)
            );
        }
    }
}

W przedstawionym przykładzie klasy dekoratorów, takie jak ErrorValidatorDecorator czy PathValidatorDecorator, pozwalają na uzupełnienie bazowego obiektu klasy UploadedFile o dodatkowe warstwy odpowiedzialności, takie jak walidacja błędów przesyłania czy sprawdzanie poprawności ścieżki docelowej. Każdy dekorator realizuje pojedynczą, jasno określoną odpowiedzialność, na przykład ErrorValidatorDecorator dba o upewnienie się, że przesłany plik nie zawiera błędów po stronie serwera, natomiast PathValidatorDecorator weryfikuje poprawność katalogu docelowego przy przenoszeniu pliku.

<?php

declare(strict_types=1);

namespace DJWeb\Framework\Http\UploadedFile;

use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UploadedFileInterface;

class UploadedFileFactory
{
    public static function createUploadedFile(
        StreamInterface|string $streamOrFile,
        int $size,
        int $error,
        ?string $clientFilename = null,
        ?string $clientMediaType = null
    ): UploadedFileInterface {
        $baseFile = new BaseUploadedFile($streamOrFile, $size, $error, $clientFilename, $clientMediaType);

        $file = new ErrorValidatorDecorator($baseFile);
        $file = new MoveStatusDecorator($file);
        return new PathValidatorDecorator($file);
    }
}

Wzorzec dekorator pozwala na elastyczne łączenie różnych funkcjonalności poprzez ich opakowywanie kolejno wokół obiektu bazowego. Przykład z klasy UploadedFileFactory pokazuje, jak w łatwy sposób można skomponować obiekt wynikowy, nakładając kolejne dekoratory: bazowy plik zostaje najpierw opakowany przez ErrorValidatorDecorator, następnie przez MoveStatusDecorator, a na koniec przez PathValidatorDecorator. Dzięki temu końcowy użytkownik otrzymuje jeden, kompleksowy obiekt, w którym poszczególne zachowania są logicznie odseparowane.

Implementacja dekoratorów jest również zgodna z wytycznymi standardów PSR-7 i PSR-17, które szczegółowo opisują sposób obsługi zapytań HTTP oraz tworzenia obiektów reprezentujących przesłane pliki. Dzięki takiemu podejściu, dekoratory umożliwiają łatwe rozszerzanie implementacji o nowe walidacje i funkcjonalności, nie naruszając przy tym integralności oryginalnych klas oraz zachowując przejrzystość kodu. Całość staje się łatwiejsza w testowaniu, utrzymaniu oraz dalszym rozwoju aplikacji zgodnej z nowoczesnymi standardami PHP.

Wzorzec projektowy Budowniczy w PHP 8.4

Wzorzec projektowy Budowniczy (Builder) pozwala krok po kroku konstruować złożone obiekty, oddzielając proces ich tworzenia od reprezentacji. Dzięki temu można łatwo tworzyć różnorodne warianty tego samego obiektu bez konieczności budowania wielu konstruktorów czy używania dużej liczby argumentów. Klasa abstrakcyjna BaseQueryBuilder, która pochodzi z rozdziału 8 ebooka o PHP 8.4, stanowi doskonałą ilustrację tego wzorca, służąc jako podstawa do budowania zapytań SQL przy użyciu intuicyjnego i czytelnego API.

W przykładzie klasy BaseQueryBuilder widoczne jest zastosowanie metody łańcuchowej (fluent interface), co jest charakterystycznym elementem wzorca Builder. Metody takie jak where(), whereLike(), whereNull() czy whereGroup() umożliwiają programiście konstruowanie złożonych warunków zapytań poprzez proste i czytelne wywołania. Każda z tych metod zwraca obiekt typu static, co umożliwia płynne łączenie kolejnych wywołań metod w jeden spójny łańcuch.

<?php

declare(strict_types=1);

namespace DJWeb\Framework\DBAL\Query\Builders;

use DJWeb\Framework\DBAL\Contracts\ConnectionContract;
use DJWeb\Framework\DBAL\Contracts\Query\ConditionContract;
use DJWeb\Framework\DBAL\Contracts\Query\QueryBuilderContract;
use DJWeb\Framework\DBAL\Query\Conditions\AndCondition;
use DJWeb\Framework\DBAL\Query\Conditions\OrCondition;
use DJWeb\Framework\DBAL\Query\Conditions\WhereCondition;
use DJWeb\Framework\DBAL\Query\Conditions\WhereGroupCondition;
use DJWeb\Framework\DBAL\Query\Conditions\WhereLikeCondition;
use DJWeb\Framework\DBAL\Query\Conditions\WhereNotNullCondition;
use DJWeb\Framework\DBAL\Query\Conditions\WhereNullCondition;

abstract class BaseQueryBuilder implements QueryBuilderContract
{
    /**
     * @var array<int, ConditionContract>
     */
    protected array $conditions = [];
    protected string $table;
    /**
     * @var array<int, int|string|float|null>
     */
    protected array $params = [];

    public function __construct(
        protected ConnectionContract $connection
    ) {
    }

    public function where(
        string $column,
        string $operator,
        mixed $value,
        bool $and = true
    ): static {
        $condition = new WhereCondition($column, $operator, $value);
        $item = $and ?
            new AndCondition($condition, count($this->conditions))
            : new OrCondition($condition);
        $this->conditions[] = $item;
        return $this;
    }

    public function whereLike(
        string $column,
        string $pattern,
        bool $and = true
    ): static {
        $condition = new WhereLikeCondition($column, $pattern);
        return $this->whereCondition($condition, $and);
    }

    public function whereNull(string $column, bool $and = true): self
    {
        $condition = new WhereNullCondition($column);
        return $this->whereCondition($condition, $and);
    }

    public function whereGroup(callable $callback, bool $and = true): self
    {
        $groupCondition = new WhereGroupCondition($this);
        $callback($groupCondition);
        return $this->whereCondition($groupCondition, $and);
    }

    private function whereCondition(
        ConditionContract $condition,
        bool $and = true
    ): static {
        $item = $and ?
            new AndCondition($condition, count($this->conditions))
            : new OrCondition($condition);
        $this->conditions[] = $item;
        return $this;
    }
}

Istotnym aspektem implementacji wzorca budowniczy w tym przykładzie jest również delegowanie odpowiedzialności poszczególnym klasom warunków, np. WhereLikeCondition. Taka struktura pozwala utrzymać przejrzystość kodu i separację logiki generowania fragmentów SQL od głównego obiektu budującego. Dzięki temu rozszerzenie o nowe typy warunków czy operatorów SQL jest znacznie prostsze i bardziej zorganizowane – wystarczy stworzyć nową klasę warunku implementującą interfejs ConditionContract.

<?php

declare(strict_types=1);

namespace DJWeb\Framework\DBAL\Query\Conditions;

use DJWeb\Framework\DBAL\Contracts\Query\ConditionContract;

readonly class WhereLikeCondition implements ConditionContract
{
    public function __construct(
        private string $column,
        private string $pattern
    ) {
    }

    public function getSQL(): string
    {
        return "{$this->column} LIKE ?";
    }

    public function getParams(): array
    {
        return [$this->pattern];
    }
}

Zastosowanie wzorca Budowniczy znacząco poprawia czytelność oraz intuicyjność kodu generującego zapytania do bazy danych. Dzięki takiej implementacji, programiści mogą łatwo budować dynamiczne i złożone zapytania, zachowując jednocześnie porządek, elastyczność i łatwość testowania kodu. W efekcie, proces rozwoju i utrzymania aplikacji bazujących na DBAL-u w PHP 8.4 staje się bardziej efektywny i profesjonalny.

Chcesz zobaczyć więcej praktycznych przykładów? Pobierz darmowy fragment ebooka 📥 lub Kup kurs php 🎓

Wzorcje projektowe w PHP 8.4 - najczęstrze pytania

Czym jest wzorzec Fasada i kiedy go stosować?
Wzorzec Fasada upraszcza dostęp do skomplikowanego systemu poprzez dostarczenie jednej klasy-kontrolera. Stosuje się go, gdy chcemy uprościć interfejs API i ukryć szczegóły implementacyjne.
Dlaczego w klasie Fasady powinno się zwracać interfejsy zamiast konkretnych klas?
Zwracanie interfejsów pozwala na elastyczność i łatwiejszą wymianę implementacji w przyszłości. Dzięki temu kod nie jest zależny od konkretnych klas, co ułatwia testowanie i utrzymanie
Jakie zalety ma wzorzec Fabryka w porównaniu do bezpośredniego tworzenia obiektów?
Fabryka eliminuje konieczność powielania logiki tworzenia obiektów w kodzie. Dzięki niej można centralnie kontrolować sposób ich inicjalizacji, co zwiększa spójność i ułatwia zarządzanie zależnościami.
Czy wzorzec Singleton jest zawsze dobrym rozwiązaniem?
Nie zawsze. Singleton ogranicza elastyczność kodu i może prowadzić do problemów z testowalnością. Powinien być stosowany tylko tam, gdzie faktycznie potrzebna jest jedna, globalna instancja klasy.
Jakie są główne zalety wzorca Dekorator?
Dekorator umożliwia dynamiczne rozszerzanie funkcjonalności obiektów bez zmiany ich oryginalnej implementacji. Pozwala to uniknąć problemów związanych z głęboką hierarchią dziedziczenia.
Czy wzorzec Budowniczy zawsze jest lepszy od bezpośredniego konstruktora?
Nie zawsze. Budowniczy jest korzystny, gdy mamy do czynienia z dużą liczbą opcjonalnych parametrów lub chcemy zachować płynność w tworzeniu obiektów. W prostych przypadkach konstruktor może być wystarczający.
Jakie nowości w PHP 8.4 ułatwiają implementację wzorców projektowych?
PHP 8.4 wprowadza ulepszenia w zarządzaniu właściwościami (``readonly``, ``getter``), lepsze wsparcie dla konstrukcji ``??=`` i inne optymalizacje, które ułatwiają implementację wzorców takich jak Singleton czy Fasada.

Zamów praktyczny kurs PHP już teraz!

Dowiedz się jak działają współczesne frameworki PHP

cover

Kup teraz 149 zł

Nasz kurs PHP w praktyce to inwestycja w Twoją karierę. Po zakupie zyskasz:

checkmark Pełny dostęp do materiałów kursu

checkmark Możliwość czytania e-booka online

checkmark Przeglądarkę kodu źródłowego z praktycznymi przykładami

checkmark Możliwość pobrania w formatach PDF/EPUB/Markdown

Najczęściej popełniane błędy przy stosowaniu wzorców projektowych w PHP

Nadużywanie Singletona i utrudnianie testowalności

Wzorzec Singleton jest często stosowany w sposób, który prowadzi do problemów z testowaniem kodu. Jeśli cała aplikacja polega na globalnym dostępie do jednej instancji klasy (getInstance()), mockowanie i testowanie jednostkowe stają się trudne. Lepszym podejściem jest zastosowanie kontenera zależności (DI), który zarządza instancjami w sposób bardziej elastyczny.

Fasada jako "bypass" dla złej architektury

Fasada jest często używana do maskowania nieoptymalnej architektury kodu zamiast jej poprawy. Jeśli za fasadą kryje się skomplikowana i źle zaprojektowana logika, to nie rozwiązuje to problemu, a jedynie go ukrywa. Dobra fasada powinna jedynie upraszczać API, a nie stać się "śmietnikiem" dla całego kodu aplikacji.

Nadużywanie Dekoratora zamiast zwykłego dziedziczenia

Choć Dekorator to potężny wzorzec, w niektórych przypadkach proste dziedziczenie może być lepszym rozwiązaniem. Problem pojawia się, gdy liczba dekoratorów rośnie i prowadzi do nieczytelnej struktury obiektów. Zbyt duża liczba warstw dekoratorów może utrudniać debugowanie, dlatego warto zachować zdrową równowagę między kompozycją a dziedziczeniem.

Fabryka zwracająca konkretne klasy zamiast interfejsów

Podstawową zaletą fabryk jest możliwość łatwej wymiany implementacji. Jeśli jednak fabryka zwraca konkretną klasę zamiast interfejsu, cała jej elastyczność zostaje zniwelowana. Kod powinien polegać na abstrakcjach, a nie na konkretnych implementacjach – dlatego zwracanie interfejsów (MailerContract zamiast Mailer) jest kluczowe.

Porównanie wzorców projektowych w PHP

Wzorzec Zastosowanie Typ wzorca Zalety Wady
Fasada Uproszczenie interfejsu skomplikowanego systemu Strukturalny Ukrywa złożoność, poprawia czytelność kodu Może ukrywać źle zaprojektowany kod
Fabryka Tworzenie obiektów w sposób bardziej elastyczny i kontrolowany Kreacyjny Ułatwia zarządzanie zależnościami, poprawia testowalność Może prowadzić do nadmiernej ilości fabryk
Singleton Zarządzanie jedną globalną instancją obiektu w aplikacji Kreacyjny Eliminuje wielokrotne tworzenie obiektu, zapewnia globalny dostęp Trudny do testowania, może prowadzić do problemów z elastycznością
Dekorator Dynamiczne rozszerzanie funkcjonalności obiektów Strukturalny Pozwala na dodawanie funkcjonalności bez zmiany kodu bazowego Zbyt wiele dekoratorów może skomplikować debugowanie
Budowniczy Stopniowa konstrukcja złożonych obiektów Kreacyjny Ułatwia tworzenie obiektów z wieloma opcjami konfiguracji Może być nadmiernym rozwiązaniem dla prostych obiektów