SOLID-principper i praksis
Anvend de fem SOLID-principper på virkelige PHP-klasser.
SOLID-principper i praksis er en gratis PHP Academy-lektion på CoddyKit. Dette er lektion 1 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i PHP Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. PHP Academy-kurset indeholder 4 lektioner i alt.
Hvorfor SOLID
SOLID består af fem designprincipper, der holder objektorienteret PHP fleksibel, testbar og modstandsdygtig over for forfald. De er ikke regler, der skal følges blindt, men heuristikker, som reducerer kobling og tydeliggør ansvar. I denne lektion anvender vi hvert princip på konkrete PHP-klasser, som du rent faktisk ville tage i brug.
- SEnkelt ansvar
- OÅben/lukket
- LLiskovs substitution
- IGrænsefladesegregering
- DAfhængighedsinversion
Enkelt ansvar
En klasse bør have én grund til at ændre sig. En klasse, der opbygger en rapport, formaterer den som HTML og sender den via e-mail, har tre grunde til at ændre sig. Opdel lagring, formatering og transport.
Herunder modellerer Invoice kun data; gengivelse og lagring foregår andre steder.
<?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: Sådan opdager du problemet
Den klassiske overtrædelse af SRP er en klasse, hvis navn indeholder and, eller en Manager, der gør alt. Hold øje med metoder, der ændrer sig af uvedkommende forretningsmæssige årsager. Hvis både en ændring af en skatteregel og en ændring af PDF-layoutet berører den samme klasse, har den for mange ansvarsområder.
Åben/lukket-princippet
Softwareenheder bør være åbne for udvidelse og lukkede for ændringer. Tilføjelse af en ny rabatetype bør ikke tvinge dig til at redigere en enorm switch. Brug polymorfi: hver regel er sin egen klasse, der implementerer en fælles grænseflade.
<?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
Liskovs substitutionsprincip
Undertyper skal kunne bruges overalt, hvor deres basistype forventes, uden at overraske den kaldende kode. Det berygtede eksempel er, at Square extends Rectangle overtræder LSP, fordi indstilling af bredden ikke stiltiende må ændre højden. Modellér dem i stedet som separate typer bag en fælles Shape-grænseflade.
<?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 og kontrakter
LSP begrænser også metodekontrakter. En undertype må svække forudsætninger og styrke efterbetingelser, men aldrig omvendt. Hvis du kaster en ny undtagelsestype, som basistypen aldrig har erklæret, eller returnerer null, hvor basistypen garanterer et objekt, overtræder du substitutionsprincippet. PHP's kovariante returtyper og kontravariante parametertyper håndhæver en del af dette på typeniveau.
Grænsefladesegregering
Klienter bør ikke være afhængige af metoder, de ikke bruger. En omfattende Worker-grænseflade med work() og eat() tvinger en RobotWorker til at lave en tom implementering af eat(). Opdel den i fokuserede rollegrænseflader, så hver implementering kun forpligter sig til det, den faktisk gør.
<?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; }
Afhængighedsinversion
Overordnet logik bør afhænge af abstraktioner, ikke konkrete detaljer. Injicér en grænseflade i stedet for en hårdkodet klasse. Så kan du udskifte en rigtig mailafsender med et testdobbelt i test og med en anden leverandør i produktion uden at ændre forbrugeren.
<?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 og containeren
Afhængighedsinversion er princippet; afhængighedsinjektion er en teknik til at opfylde det. En DI-container (PSR-11) kobler den konkrete SmtpMailer til Mailer-abstraktionen i kompositionsroden. Forbrugeren skriver aldrig new SmtpMailer(), så retningen for afhængigheden i kildekoden peger mod abstraktionen og ikke detaljen.
<?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 i samspil
Principperne styrker hinanden. ISP holder grænsefladerne små, så DIP-injektioner forbliver fokuserede; OCP afhænger af polymorfi, som LSP holder sikker; og SRP giver dig de små klasser, der gør alt dette muligt. Sigt efter høj sammenhæng internt og løs kobling mellem komponenter.
- Overabstrahér ikke en klasse, der kun bruges ét sted.
- Indfør en grænseflade, når der kommer en anden implementering eller et testdobbelt.
Pragmatisme
SOLID er et middel, ikke et mål. En for tidlig grænseflade med én implementering tilføjer et ekstra abstraktionslag uden gevinst ("spekulativ generalisering"). Anvend principperne, når en ændring faktisk opstår eller tydeligvis er nært forestående. Refaktorering mod SOLID er billig, når du har test; blind refaktorering mod SOLID fra dag ét er spild.
Hurtigt tjek
Hvilket princip overtræder problemet med nedarvningen fra Square til Rectangle?
Opsamling
Du har anvendt alle fem SOLID-principper på ægte PHP:
- SRP: én grund til at ændre sig pr. klasse.
- OCP: udvid via nye polymorfe klasser, ikke ved at redigere en switch.
- LSP: undertyper overholder basiskontrakten.
- ISP: små rollegrænseflader frem for omfattende grænseflader.
- DIP: vær afhængig af abstraktioner, og indsæt detaljer i kompositionsroden.
Brug dem til at styre refaktorering, ikke som ceremoni.
Lær PHP med en AI-underviser — gratis
Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.
- Kurser
- 49
- Lektioner
- 195
Ofte stillede spørgsmål
Er lektionen “SOLID-principper i praksis” gratis?
Ja — hele teksten til “SOLID-principper i praksis” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af PHP Academy-kurset, skal du opgradere til CoddyKit PRO. PHP Academy-kurset indeholder 4 lektioner i alt.
Hvad lærer jeg i “SOLID-principper i praksis”?
Anvend de fem SOLID-principper på virkelige PHP-klasser. Du øver dig i PHP Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.
Skal jeg have erfaring for at begynde på PHP Academy?
Der kræves ingen tidligere erfaring. PHP Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 1 af 4.
Hvor lang tid tager lektionen “SOLID-principper i praksis”?
De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.
Kan jeg skrive og køre kode i denne PHP Academy-lektion?
Ja. Alle PHP Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.
Alle lektioner i dette kursus
- SOLID-principper i praksis
- Creational patterns: Factory, Builder, Singleton
- Structural patterns: Adapter, Decorator, Facade
- Behavioral patterns: Strategy, Observer, Command