0Pricing
PHP Academy · Lekcja

Zasady SOLID w praktyce

Proszę zastosować pięć zasad SOLID w rzeczywistych klasach PHP.

Zasady SOLID w praktyce to bezpłatna lekcja PHP Academy na CoddyKit. To lekcja 1 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.

Dlaczego SOLID

SOLID to pięć zasad projektowania, które sprawiają, że obiektowy PHP pozostaje elastyczny, testowalny i odporny na degradację. Nie są to reguły, których należy ślepo przestrzegać, lecz heurystyki zmniejszające sprzężenie i porządkujące odpowiedzialności. W tej lekcji zastosują Państwo każdą zasadę do konkretnych klas PHP, które rzeczywiście można wdrożyć.

  • Single Responsibility
  • Open/Closed
  • Liskov Substitution
  • Interface Segregation
  • Dependency Inversion

Zasada jednej odpowiedzialności

Klasa powinna mieć jeden powód do zmiany. Klasa, która tworzy raport, formatuje go jako HTML i wysyła pocztą elektroniczną, ma trzy powody do zmiany. Należy rozdzielić utrwalanie danych, formatowanie i transport.

Poniżej Invoice modeluje wyłącznie dane; renderowanie i zapisywanie znajdują się w innych miejscach.

<?php
final class Invoice {
    public function __construct(
        public readonly string $number,
        public readonly int $cents
    ) {}
    public function total(): float { return $this->cents / 100; }
}

final class InvoiceRenderer {
    public function toText(Invoice $i): string {
        return sprintf('Invoice %s: $%.2f', $i->number, $i->total());
    }
}

$i = new Invoice('INV-1', 12500);
echo (new InvoiceRenderer())->toText($i), PHP_EOL;

SRP: rozpoznawanie problemu

Klasyczne naruszenie SRP to klasa, której nazwa zawiera słowo and, albo klasa Manager, która robi wszystko. Należy zwracać uwagę na metody zmieniające się z niezwiązanych ze sobą powodów biznesowych. Jeśli zmiana reguły podatkowej i zmiana układu PDF wymagają modyfikacji tej samej klasy, ma ona zbyt wiele odpowiedzialności.

Zasada otwarte-zamknięte

Elementy oprogramowania powinny być otwarte na rozszerzenia, ale zamknięte na modyfikacje. Dodanie nowego typu rabatu nie powinno wymagać edycji ogromnej instrukcji switch. Należy używać polimorfizmu: każda reguła powinna być osobną klasą implementującą wspólny interfejs.

<?php
interface Discount { public function apply(float $total): float; }

final class PercentOff implements Discount {
    public function __construct(private float $pct) {}
    public function apply(float $t): float { return $t * (1 - $this->pct / 100); }
}
final class FlatOff implements Discount {
    public function __construct(private float $amount) {}
    public function apply(float $t): float { return max(0, $t - $this->amount); }
}

function checkout(float $total, Discount ...$discounts): float {
    foreach ($discounts as $d) { $total = $d->apply($total); }
    return $total;
}
echo checkout(100, new PercentOff(10), new FlatOff(5)), PHP_EOL; // 85

Podstawienie Liskov

Typów pochodnych należy używać wszędzie tam, gdzie oczekiwany jest ich typ bazowy, bez zaskakiwania wywołującego. Słynny przykład: Square extends Rectangle łamie zasadę LSP, ponieważ ustawienie szerokości nie powinno po cichu zmieniać wysokości. Lepiej modelować je jako osobne typy za wspólnym interfejsem Shape.

<?php
interface Shape { public function area(): float; }

final class Rectangle implements Shape {
    public function __construct(private float $w, private float $h) {}
    public function area(): float { return $this->w * $this->h; }
}
final class Square implements Shape {
    public function __construct(private float $side) {}
    public function area(): float { return $this->side ** 2; }
}

$shapes = [new Rectangle(2, 3), new Square(4)];
foreach ($shapes as $s) { echo $s->area(), PHP_EOL; }

LSP a kontrakty

LSP ogranicza również kontrakty metod. Typ pochodny może osłabiać warunki wstępne i wzmacniać warunki końcowe, ale nigdy odwrotnie. Rzucanie nowym typem wyjątku, którego typ bazowy nie deklarował, albo zwracanie null tam, gdzie typ bazowy gwarantuje obiekt, narusza zasadę podstawienia. Kowariantne typy zwracane i kontrawariantne typy parametrów w PHP wymuszają część tych zasad na poziomie typów.

Segregacja interfejsów

Klienci nie powinni zależeć od metod, których nie używają. Rozbudowany interfejs Worker z metodami work() i eat() zmusza RobotWorker do utworzenia zaślepki dla eat(). Należy podzielić go na skoncentrowane na rolach interfejsy, aby każdy implementujący je typ zobowiązywał się tylko do tego, co rzeczywiście robi.

<?php
interface Workable { public function work(): string; }
interface Feedable { public function eat(): string; }

final class Human implements Workable, Feedable {
    public function work(): string { return 'coding'; }
    public function eat(): string { return 'lunch'; }
}
final class Robot implements Workable {
    public function work(): string { return 'welding'; }
}

foreach ([new Human(), new Robot()] as $w) { echo $w->work(), PHP_EOL; }

Odwrócenie zależności

Polityka wysokiego poziomu powinna zależeć od abstrakcji, a nie od konkretnych szczegółów. Należy wstrzykiwać interfejs, a nie zakodowaną na stałe klasę. Dzięki temu można zastąpić rzeczywisty moduł pocztowy atrapą w testach, a w środowisku produkcyjnym użyć innego dostawcy bez modyfikowania kodu użytkownika.

<?php
interface Mailer { public function send(string $to, string $body): void; }

final class SmtpMailer implements Mailer {
    public function send(string $to, string $body): void {
        echo "SMTP -> $to: $body" . PHP_EOL;
    }
}

final class SignupService {
    public function __construct(private Mailer $mailer) {}
    public function register(string $email): void {
        $this->mailer->send($email, 'Welcome!');
    }
}

(new SignupService(new SmtpMailer()))->register('a@b.com');

DIP a kontener

Odwrócenie zależności jest zasadą, a wstrzykiwanie zależności to jedna z technik jej realizacji. Kontener DI (PSR-11) łączy konkretną klasę SmtpMailer z abstrakcją Mailer w miejscu składania zależności. Kod korzystający z zależności nigdy nie wywołuje new SmtpMailer(), więc kierunek zależności w kodzie źródłowym wskazuje na abstrakcję, a nie na szczegół implementacyjny.

<?php
// Composition root wiring (pseudo-container)
$bindings = [
    Mailer::class => fn() => new SmtpMailer(),
];
$resolve = fn(string $id) => $bindings[$id]();

$service = new SignupService($resolve(Mailer::class));
$service->register('user@example.com');

SOLID razem

Zasady wzajemnie się wzmacniają. ISP utrzymuje małe interfejsy, dzięki czemu wstrzykiwanie zależności zgodne z DIP pozostaje skoncentrowane; OCP opiera się na polimorfizmie, którego bezpieczeństwo zapewnia LSP; SRP dostarcza małych klas umożliwiających to wszystko. Należy dążyć do spójności wewnątrz i luźnego sprzężenia między elementami.

  • Nie należy tworzyć nadmiernych abstrakcji dla klasy używanej tylko raz.
  • Interfejs należy wprowadzić, gdy pojawi się druga implementacja lub obiekt testowy.

Pragmatyzm

SOLID jest środkiem, a nie celem. Przedwczesny interfejs z jedną implementacją dodaje warstwę pośrednią bez żadnej korzyści („speculative generality”). Zasady należy stosować, gdy zmiana rzeczywiście nadchodzi lub jest wyraźnie bliska. Refaktoryzacja w kierunku SOLID jest tania, gdy istnieją testy; ślepe dążenie do SOLID od pierwszego dnia jest stratą czasu.

Szybkie sprawdzenie

Jaką zasadę narusza problem dziedziczenia Square/Rectangle?

Powtórzenie

Zastosowali Państwo wszystkie pięć zasad SOLID w rzeczywistym kodzie PHP:

  • SRP: jedna przyczyna zmiany na klasę.
  • OCP: rozszerzanie za pomocą nowych klas polimorficznych zamiast edytowania instrukcji switch.
  • LSP: typy pochodne respektują kontrakt typu bazowego.
  • ISP: małe interfejsy ról zamiast rozbudowanych interfejsów.
  • DIP: zależność od abstrakcji i wstrzykiwanie szczegółów w miejscu składania zależności.

Należy wykorzystywać je jako wskazówki podczas refaktoryzacji, a nie jako formalność.

Często zadawane pytania

Czy lekcja „Zasady SOLID w praktyce” jest bezpłatna?

Tak — pełny tekst „Zasady SOLID w praktyce” 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 „Zasady SOLID w praktyce”?

Proszę zastosować pięć zasad SOLID w rzeczywistych klasach PHP. Ć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 1 z 4.

Ile czasu zajmuje lekcja „Zasady SOLID w praktyce”?

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

  1. Zasady SOLID w praktyce
  2. Wzorce kreacyjne: Factory, Builder, Singleton
  3. Wzorce strukturalne: Adapter, Decorator, Facade
  4. Wzorce behawioralne: Strategy, Observer, Command
← Powrót do PHP Academy