SOLID-principes in de praktijk
Pas de vijf SOLID-principes toe op echte PHP-klassen.
SOLID-principes in de praktijk is een gratis PHP Academy-les op CoddyKit. Dit is les 1 van 4. Je kunt de volledige les hieronder gratis lezen en daarna in de browser praktisch oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject PHP Academy. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus PHP Academy bevat in totaal 4 lessen.
Waarom SOLID?
SOLID bestaat uit vijf ontwerpprincipes die objectgeoriënteerde PHP flexibel, toetsbaar en bestand tegen codeverval houden. Het zijn geen regels die je blind moet volgen, maar heuristieken die koppeling verminderen en verantwoordelijkheden verduidelijken. In deze les passen we elk principe toe op concrete PHP-klassen die je daadwerkelijk zou uitleveren.
- SEnkelvoudige verantwoordelijkheid
- OOpen-gesloten
- LSubstitutie volgens Liskov
- IScheiding van interfaces
- DAfhankelijkheidsinversie
Enkelvoudige verantwoordelijkheid
Een klasse hoort één reden om te wijzigen te hebben. Een klasse die een rapport opbouwt, het als HTML opmaakt en het e-mailt, heeft drie redenen om te wijzigen. Splits opslag, opmaak en verzending op.
Hieronder modelleert Invoice alleen gegevens; weergave en opslag vinden elders plaats.
<?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: de codegeur herkennen
De klassieke SRP-schending is een klasse waarvan de naam en bevat, of een Manager die alles doet. Let op methoden die veranderen om niet-gerelateerde bedrijfsredenen. Als een wijziging van een belastingregel en een wijziging van een pdf-indeling dezelfde klasse raken, heeft die te veel verantwoordelijkheden.
Open-geslotenprincipe
Software-entiteiten horen open te staan voor uitbreiding en gesloten te zijn voor wijziging. Het toevoegen van een nieuw kortingstype hoort geen wijzigingen aan een enorme switch af te dwingen. Gebruik polymorfisme: elke regel is een eigen klasse die een gedeeld contract implementeert.
<?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
Substitutie volgens Liskov
Subtypen moeten overal bruikbaar zijn waar hun basistype wordt verwacht, zonder de aanroeper te verrassen. Het beruchte voorbeeld: Square extends Rectangle schendt LSP, omdat het instellen van de breedte de hoogte niet stilzwijgend mag veranderen. Modelleer ze liever als afzonderlijke typen achter een gemeenschappelijk Shape-contract.
<?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 en contracten
LSP beperkt ook contracten van methoden. Een subtype mag voorwaarden vooraf verzwakken en voorwaarden achteraf versterken, maar nooit andersom. Een nieuw uitzonderingstype opwerpen dat het basistype nooit heeft gedeclareerd, of null teruggeven waar het basistype een object garandeert, schendt de substitueerbaarheid. Covariante retourtypen en contravariante parametertypen van PHP dwingen een deel hiervan op typeniveau af.
Scheiding van interfaces
Afnemers horen niet afhankelijk te zijn van methoden die ze niet gebruiken. Een omvangrijk Worker-contract met work() en eat() dwingt een RobotWorker om eat() als lege methode te implementeren. Splits het op in gerichte rolcontracten, zodat elke implementatie alleen belooft wat ze daadwerkelijk doet.
<?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; }
Afhankelijkheidsinversie
Beleid op hoog niveau hoort afhankelijk te zijn van abstracties, niet van concrete details. Injecteer een abstractie, geen hardgecodeerde klasse. Zo kun je in toetsen een echte mailverzender vervangen door een nepversie en in productie een andere leverancier gebruiken zonder de afnemer aan te passen.
<?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 en de container
Inversie van afhankelijkheden is het principe; injectie van afhankelijkheden is een techniek om eraan te voldoen. Een DI-container (PSR-11) koppelt de concrete SmtpMailer aan de abstractie Mailer bij het samenstellingspunt. De afnemer schrijft nooit new SmtpMailer(), waardoor de richting van de afhankelijkheid in de broncode naar de abstractie wijst en niet naar het detail.
<?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 samen
De principes versterken elkaar. ISP houdt contracten klein, zodat injecties volgens DIP gericht blijven; OCP steunt op polymorfisme dat LSP veilig houdt; SRP levert de kleine klassen waarmee dit allemaal mogelijk wordt. Streef naar hoge samenhang binnenin en losse koppeling ertussen.
- Maak geen overmatige abstractie van een klasse die maar één keer wordt gebruikt.
- Introduceer een contract wanneer er een tweede implementatie of een toetsdubbel verschijnt.
Pragmatisme
SOLID is een middel, geen doel. Een voortijdig contract met één implementatie voegt zonder opbrengst een omweg toe (“speculatieve generalisatie”). Pas de principes toe wanneer verandering zich daadwerkelijk aandient of duidelijk op komst is. Refactoren richting SOLID is goedkoop zodra je toetsen hebt; er vanaf dag één blind naartoe refactoren is verspilling.
Korte controle
Welk principe schendt het overervingsprobleem van Square en Rectangle?
Terugblik
Je hebt alle vijf SOLID-principes toegepast op echte PHP:
- SRP: één reden om te wijzigen per klasse.
- OCP: uitbreiden via nieuwe polymorfe klassen, niet via wijzigingen aan een switch.
- LSP: subtypen houden zich aan het contract van het basistype.
- ISP: kleine rolcontracten in plaats van omvangrijke.
- DIP: afhankelijk zijn van abstracties en details injecteren bij het samenstellingspunt.
Gebruik ze om refactoring te sturen, niet als ceremonie.
Leer PHP met een AI-tutor — gratis
Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.
- Cursussen
- 49
- Lessen
- 195
Veelgestelde vragen
Is de les “SOLID-principes in de praktijk” gratis?
Ja — de volledige tekst van “SOLID-principes in de praktijk” kun je hier gratis op het web lezen. Als je interactief wilt oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is, en de rest van de cursus PHP Academy wilt ontgrendelen, kun je upgraden naar CoddyKit PRO. De cursus PHP Academy bevat in totaal 4 lessen.
Wat leer ik in “SOLID-principes in de praktijk”?
Pas de vijf SOLID-principes toe op echte PHP-klassen. Je oefent met PHP Academy door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.
Heb ik ervaring nodig om met PHP Academy te beginnen?
Ervaring vooraf is niet nodig. PHP Academy op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 1 van 4.
Hoe lang duurt de les “SOLID-principes in de praktijk”?
De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.
Kan ik code schrijven en uitvoeren in deze les over PHP Academy?
Ja. Elke les over PHP Academy bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.
Alle lessen in deze cursus
- SOLID-principes in de praktijk
- Creational patterns: Factory, Builder, Singleton
- Structural patterns: Adapter, Decorator, Facade
- Behavioral patterns: Strategy, Observer, Command