statt an der Theorie
Die meisten Erklärungen zu SOLID-Prinzipien arbeiten mit Formen, Enten oder Tieren und lassen offen, wie das in echtem Anwendungscode aussieht. Dieser Artikel nimmt stattdessen eine durchgehende Order-Processing-Pipeline in PHP 8.4, an der jedes der fünf Prinzipien ein konkretes, nachvollziehbares Wartbarkeitsproblem löst: eine Order-Klasse, die zu viel macht, Rabattregeln, die zu einer if/else-Kaskade wuchern, ein Payment-Gateway, das seinen eigenen Vertrag bricht, ein zu fettes Repository-Interface und ein Notification-Service, der zu eng an einen konkreten Mailer gekoppelt ist.
Inhaltsverzeichnis
- 1. Warum SOLID-Prinzipien ein Wartbarkeits-Werkzeug sind, kein akademisches Konzept
- 2. Single Responsibility Principle: eine Order-Klasse, die zu viel macht
- 3. Open/Closed Principle: Rabattregeln ohne wachsende if/else-Kaskaden
- 4. Liskov Substitution Principle: wenn ein Payment-Gateway seinen Vertrag bricht
- 5. Interface Segregation Principle: ein zu fettes Repository-Interface aufteilen
- 6. Dependency Inversion Principle: der Notification-Service und die Abstraktion
- 7. SOLID-Prinzipien im Zusammenspiel: die gesamte Order-Processing-Pipeline
- 8. Wann SOLID übertrieben ist: pragmatische Grenzen ziehen
- 9. Verstoß vs. SOLID-konforme Lösung im direkten Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum SOLID-Prinzipien ein Wartbarkeits-Werkzeug sind, kein akademisches Konzept
Die SOLID-Prinzipien wurden lange als Prüfungsstoff behandelt: fünf Buchstaben, fünf Definitionen, auswendig gelernt und selten in echtem Code wiedererkannt. Das liegt vor allem daran, dass die klassischen Erklärungen mit Formen, Enten oder Vögeln arbeiten, ein Rectangle, das von Square erbt, oder ein Bird, das nicht fliegen kann. Diese Beispiele sind didaktisch bequem, aber sie zeigen nicht, wie sich ein Verstoß gegen die SOLID-Prinzipien in einer echten Anwendung anfühlt: als Klasse, die bei jeder Anforderung angefasst werden muss, als Testfall, der drei Datenbank-Mocks braucht, um eine einzige Methode zu prüfen, oder als Bugfix, der an einer Stelle etwas repariert und an drei anderen etwas kaputt macht.
Dieser Artikel verzichtet deshalb komplett auf Formen und Tiere. Stattdessen begleitet eine einzige, zusammenhängende Order-Processing-Pipeline den gesamten Text: eine Order-Klasse, Rabattregeln, Payment-Gateways, Repositories und ein Notification-Service. Jedes der fünf SOLID-Prinzipien, Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation und Dependency Inversion, löst darin ein konkretes Problem, das beim Wachsen einer echten Applikation zwangsläufig entsteht. Der Code ist PHP 8.4 mit strict_types, Constructor Property Promotion und readonly-Eigenschaften, framework-unabhängig und ohne Bezug zu einem bestimmten Stack.
Wichtig vorab: SOLID-Prinzipien sind kein Selbstzweck. Sie sind ein Werkzeugkasten, um Kopplung zu reduzieren und Verantwortlichkeiten klar zu trennen, dort, wo eine Anwendung tatsächlich wächst und sich ändert. Wo dieses Wachstum nicht stattfindet, kann striktes Befolgen aller fünf Prinzipien zu unnötiger Indirektion führen, ein Punkt, auf den Abschnitt 8 gezielt eingeht. Die SOLID-Prinzipien sind ein Kompass für Design-Entscheidungen, keine Checkliste, die man blind abarbeitet.
2. Single Responsibility Principle: eine Order-Klasse, die zu viel macht
Das Single Responsibility Principle, das erste der fünf SOLID-Prinzipien, besagt, dass eine Klasse genau einen Grund haben sollte, sich zu ändern. In der Praxis beginnt fast jede Order-Processing-Anwendung mit einer Order-Klasse, die genau das nicht einhält: Sie berechnet Summen, validiert Eingaben, persistiert sich selbst in der Datenbank und verschickt am Ende noch eine Versand-Bestätigungs-Mail. Vier fachlich völlig unterschiedliche Änderungsgründe, gebündelt in einer einzigen Klasse. Ändert sich das E-Mail-Template, muss die Order-Klasse angefasst werden. Ändert sich das Datenbankschema, ebenfalls. Ändert sich die Validierungsregel für Mindestbestellwerte, ebenfalls.
Der Verstoß gegen die SOLID-Prinzipien zeigt sich hier nicht als Syntaxfehler, sondern als Reibung: Ein Unit-Test für die Summenberechnung muss eine Datenbankverbindung mocken, obwohl er mit Persistenz nichts zu tun hat. Ein Entwickler, der nur die Versand-Mail anpassen soll, liest zwangsläufig Validierungslogik mit, die ihn nicht betrifft. Die Lösung nach dem Single Responsibility Principle trennt die vier Verantwortlichkeiten in vier Klassen: Order als reines Datenobjekt, OrderValidator für Geschäftsregeln, OrderRepository für Persistenz und OrderShippedNotifier für die Benachrichtigung. Jede Klasse hat jetzt genau einen Grund, sich zu ändern, und genau das ist der Kern des Prinzips.
declare(strict_types=1);
// BEFORE: one class doing persistence, validation and notification
final class Order
{
private array $items = [];
public function __construct(
private readonly int $customerId,
) {}
public function addItem(string $sku, int $quantity, string $currency, int $unitPriceMinor): void
{
$this->items[] = compact('sku', 'quantity', 'currency', 'unitPriceMinor');
}
public function total(): int
{
return array_sum(array_map(
static fn (array $item): int => $item['unitPriceMinor'] * $item['quantity'],
$this->items,
));
}
// Validation logic mixed into the data class
public function validate(): void
{
if ($this->items === []) {
throw new \DomainException('Order must contain at least one item.');
}
}
// Persistence mixed into the data class
public function save(\PDO $connection): void
{
$connection->prepare('INSERT INTO orders (customer_id, total) VALUES (?, ?)')
->execute([$this->customerId, $this->total()]);
}
// Notification mixed into the data class
public function sendShippedMail(\Closure $mailer): void
{
$mailer("Your order for customer {$this->customerId} has shipped.");
}
}
3. Open/Closed Principle: Rabattregeln ohne wachsende if/else-Kaskaden
Das Open/Closed Principle verlangt, dass eine Klasse offen für Erweiterung, aber geschlossen für Modifikation ist. Ein klassischer Verstoß gegen die SOLID-Prinzipien entsteht bei Rabattregeln fast automatisch: Der erste Rabatt, ein fester Prozentsatz für Neukunden, wird als einzelnes if in eine calculateDiscount-Methode geschrieben. Der zweite Rabatt, ein Mengenrabatt ab zehn Artikeln, kommt als elseif dazu. Nach einem Jahr hat die Methode zwölf Verzweigungen, jede neue Marketingaktion erfordert eine Änderung an genau dieser Methode, und jede Änderung riskiert, eine der bestehenden elf Regeln versehentlich zu brechen.
Die SOLID-Prinzipien-konforme Lösung ersetzt die if/else-Kaskade durch ein DiscountRule-Interface mit einer einzigen Methode, etwa apply(Order $order): int. Jede konkrete Rabattregel, NewCustomerDiscount, BulkQuantityDiscount, SeasonalDiscount, implementiert dieses Interface als eigene, kleine Klasse. Der aufrufende Code iteriert über eine Liste von Regeln und summiert die Ergebnisse, ohne zu wissen, wie viele Regeln es gibt oder was sie im Detail tun. Eine neue Rabattaktion bedeutet: eine neue Klasse hinzufügen, keine bestehende Methode anfassen. Genau das ist mit "offen für Erweiterung, geschlossen für Modifikation" gemeint.
declare(strict_types=1);
interface DiscountRule
{
// Returns the discount amount in minor currency units (e.g. cents)
public function apply(Order $order): int;
}
final readonly class NewCustomerDiscount implements DiscountRule
{
public function __construct(private bool $isNewCustomer, private int $flatDiscountMinor) {}
public function apply(Order $order): int
{
return $this->isNewCustomer ? $this->flatDiscountMinor : 0;
}
}
final readonly class BulkQuantityDiscount implements DiscountRule
{
public function __construct(private int $thresholdQuantity, private float $percentOff) {}
public function apply(Order $order): int
{
if ($order->totalQuantity() < $this->thresholdQuantity) {
return 0;
}
return (int) round($order->total() * $this->percentOff);
}
}
// Adding a new promotion never touches this class again
final readonly class DiscountEngine
{
/** @param DiscountRule[] $rules */
public function __construct(private array $rules) {}
public function totalDiscount(Order $order): int
{
return array_sum(array_map(
static fn (DiscountRule $rule): int => $rule->apply($order),
$this->rules,
));
}
}
4. Liskov Substitution Principle: wenn ein Payment-Gateway seinen Vertrag bricht
Das Liskov Substitution Principle ist unter den fünf SOLID-Prinzipien das am häufigsten falsch verstandene: Es geht nicht nur darum, dass eine Subklasse dieselbe Methodensignatur implementiert, sondern dass sie sich an denselben Vertrag hält, den die Basisklasse verspricht. Ein typisches Beispiel in einer Order-Processing-Pipeline: ein abstraktes PaymentGateway mit einer Methode charge(int $amountMinor): PaymentResult, das für alle Implementierungen verspricht, entweder ein erfolgreiches Ergebnis zurückzugeben oder eine PaymentDeclinedException zu werfen. Eine Subklasse LegacyWireTransferGateway verletzt diesen Vertrag, wenn sie bei einer Ablehnung stattdessen null zurückgibt oder eine völlig andere Exception wirft, die der aufrufende Code nicht erwartet.
Der Effekt ist tückisch, weil der Verstoß gegen die SOLID-Prinzipien nicht beim Kompilieren auffällt, sondern erst zur Laufzeit, meist genau dann, wenn die Legacy-Gateway-Instanz eingesetzt wird und niemand mehr damit rechnet. Der aufrufende Code, der ein PaymentGateway erwartet, kann sich nicht mehr blind auf den Vertrag verlassen und muss Sonderfälle für einzelne Subklassen einbauen, genau die Art von Kopplung, die Polymorphismus eigentlich vermeiden soll. Die korrekte Lösung: Die Basisklasse oder das Interface muss den Vertrag explizit und vollständig festlegen, entweder per Typsystem, per Exception-Hierarchie oder per dokumentiertem Verhalten, und jede Subklasse muss sich exakt daran halten, ohne Sonderfälle für sich selbst zu beanspruchen.
declare(strict_types=1);
final class PaymentDeclinedException extends \RuntimeException {}
interface PaymentGateway
{
// Contract: returns a successful PaymentResult or throws PaymentDeclinedException.
// Must never return null and must never throw any other exception type.
public function charge(int $amountMinor): PaymentResult;
}
final readonly class PaymentResult
{
public function __construct(
public string $transactionId,
public int $chargedAmountMinor,
) {}
}
final readonly class CreditCardGateway implements PaymentGateway
{
public function __construct(private CardProcessorClient $client) {}
public function charge(int $amountMinor): PaymentResult
{
$response = $this->client->submit($amountMinor);
if (!$response->approved) {
throw new PaymentDeclinedException('Card declined by processor.');
}
return new PaymentResult($response->transactionId, $amountMinor);
}
}
// FIXED: honors the exact same contract as CreditCardGateway, no null, no custom exception
final readonly class LegacyWireTransferGateway implements PaymentGateway
{
public function __construct(private WireTransferClient $client) {}
public function charge(int $amountMinor): PaymentResult
{
$status = $this->client->initiateTransfer($amountMinor);
if ($status === WireTransferClient::STATUS_REJECTED) {
throw new PaymentDeclinedException('Wire transfer rejected by bank.');
}
return new PaymentResult($status->reference, $amountMinor);
}
}
5. Interface Segregation Principle: ein zu fettes Repository-Interface aufteilen
Das Interface Segregation Principle verlangt, dass Clients nicht gezwungen werden sollten, von Methoden abzuhängen, die sie nicht nutzen. In der Order-Processing-Pipeline entsteht der typische Verstoß gegen die SOLID-Prinzipien durch ein einziges, gut gemeintes OrderRepositoryInterface, das mit der Zeit anwächst: save(), findById(), findByCustomer(), delete(), archiveOlderThan(), exportToCsv(). Ein ReportingService, der nur Bestellungen lesen will, muss eine Testdouble-Implementierung bauen, die auch delete() und archiveOlderThan() bereitstellt, obwohl er diese Methoden nie aufruft. Jede Änderung an der Archivierungslogik zwingt den Reporting-Code, seine Mocks anzupassen, obwohl er fachlich nichts damit zu tun hat.
Die SOLID-Prinzipien-konforme Lösung teilt das fette Interface in kleinere, rollenspezifische Interfaces auf: OrderReader mit findById() und findByCustomer(), OrderWriter mit save() und delete(), OrderArchiver mit archiveOlderThan(). Eine konkrete DoctrineOrderRepository-Klasse kann weiterhin alle drei Interfaces implementieren, aber der ReportingService hängt nur noch von OrderReader ab. Er sieht archivierungsrelevante Methoden gar nicht mehr, sein Test braucht kein Mock für delete(), und Änderungen an der Archivierung berühren seinen Code nicht mehr.
declare(strict_types=1);
interface OrderReader
{
public function findById(int $orderId): ?Order;
/** @return Order[] */
public function findByCustomer(int $customerId): array;
}
interface OrderWriter
{
public function save(Order $order): void;
public function delete(int $orderId): void;
}
interface OrderArchiver
{
public function archiveOlderThan(\DateTimeImmutable $cutoff): int;
}
// Reporting only ever depends on the narrow interface it actually needs
final readonly class ReportingService
{
public function __construct(private OrderReader $orders) {}
public function customerTotalSpend(int $customerId): int
{
return array_sum(array_map(
static fn (Order $order): int => $order->total(),
$this->orders->findByCustomer($customerId),
));
}
}
// One concrete class can still implement all three narrow interfaces
final readonly class DoctrineOrderRepository implements OrderReader, OrderWriter, OrderArchiver
{
public function __construct(private \PDO $connection) {}
public function findById(int $orderId): ?Order
{
// Implementation detail omitted for brevity
return null;
}
/** @return Order[] */
public function findByCustomer(int $customerId): array
{
return [];
}
public function save(Order $order): void {}
public function delete(int $orderId): void {}
public function archiveOlderThan(\DateTimeImmutable $cutoff): int
{
return 0;
}
}
6. Dependency Inversion Principle: der Notification-Service und die Abstraktion
Das Dependency Inversion Principle ist das letzte, aber vielleicht folgenreichste der fünf SOLID-Prinzipien: High-Level-Module sollten nicht von Low-Level-Modulen abhängen, beide sollten von Abstraktionen abhängen. Ein häufiger Verstoß gegen die SOLID-Prinzipien in der Order-Processing-Pipeline ist ein OrderShippedNotifier, der direkt eine konkrete SmtpMailer-Klasse instanziiert und aufruft. Solange nur E-Mails verschickt werden, funktioniert das. Sobald ein Kunde stattdessen eine SMS oder eine Push-Benachrichtigung erwartet, muss der OrderShippedNotifier selbst geändert werden, obwohl seine eigentliche Aufgabe, entscheiden wann benachrichtigt wird, unverändert bleibt.
Die Lösung nach dem Dependency Inversion Principle führt eine Abstraktion ein, ein NotificationChannel-Interface mit einer Methode send(string $recipient, string $message): void. Der OrderShippedNotifier hängt nur noch von diesem Interface ab, nicht von SmtpMailer oder einer bestimmten SMS-API. Über Constructor Property Promotion wird die konkrete Implementierung von außen injiziert, typischerweise über einen Dependency-Injection-Container. Ein Wechsel von E-Mail zu SMS bedeutet: eine neue Klasse, die NotificationChannel implementiert, in der Container-Konfiguration eintragen, keine Zeile im OrderShippedNotifier anfassen. Genau diese Umkehr der Abhängigkeitsrichtung, vom konkreten Detail zur Abstraktion, ist der Kern der SOLID-Prinzipien in diesem letzten Punkt.
declare(strict_types=1);
interface NotificationChannel
{
public function send(string $recipient, string $message): void;
}
final readonly class SmtpNotificationChannel implements NotificationChannel
{
public function __construct(private SmtpMailer $mailer) {}
public function send(string $recipient, string $message): void
{
$this->mailer->send($recipient, 'Your order has shipped', $message);
}
}
final readonly class SmsNotificationChannel implements NotificationChannel
{
public function __construct(private SmsGatewayClient $client) {}
public function send(string $recipient, string $message): void
{
$this->client->sendText($recipient, $message);
}
}
// High-level policy depends only on the abstraction, never on a concrete channel
final readonly class OrderShippedNotifier
{
public function __construct(private NotificationChannel $channel) {}
public function notify(Order $order, string $recipient): void
{
$this->channel->send(
$recipient,
"Order #{$order->id()} for customer {$order->customerId()} has shipped.",
);
}
}
7. SOLID-Prinzipien im Zusammenspiel: die gesamte Order-Processing-Pipeline
Einzeln betrachtet lösen die SOLID-Prinzipien jeweils ein isoliertes Problem. Ihre eigentliche Kraft zeigt sich erst, wenn man die gesamte Order-Processing-Pipeline als Ganzes betrachtet: Eine OrderProcessor-Klasse, die eine Order validiert, Rabatte über die DiscountEngine anwendet, über ein PaymentGateway abrechnet, über einen OrderWriter persistiert und über einen NotificationChannel benachrichtigt, ist selbst nur noch Orchestrierung. Sie kennt keine einzige konkrete Implementierung, sondern ausschließlich die fünf Abstraktionen, die die vorherigen Abschnitte eingeführt haben.
Diese Orchestrierungsklasse ist ein direktes Resultat aller fünf SOLID-Prinzipien gleichzeitig: Sie hat genau eine Verantwortung, den Ablauf zu steuern (SRP). Neue Rabattregeln oder Payment-Gateways werden hinzugefügt, ohne sie anzufassen (OCP). Jede Payment-Gateway-Implementierung kann austauschbar eingesetzt werden, weil sie sich an denselben Vertrag hält (LSP). Sie hängt nur von den schmalen Interfaces ab, die sie tatsächlich braucht, nicht von einem fetten Repository (ISP). Und sie hängt ausschließlich von Abstraktionen ab, nie von konkreten Klassen (DIP). Das Ergebnis ist eine Klasse, die in Isolation testbar ist, mit einfachen Test-Doubles für jede der fünf Abhängigkeiten, ganz ohne echte Datenbank oder echten Mailversand.
declare(strict_types=1);
// The orchestration class combines all five SOLID principles at once:
// it depends only on abstractions, and has exactly one job: coordinate the flow.
final readonly class OrderProcessor
{
public function __construct(
private OrderValidator $validator,
private DiscountEngine $discounts,
private PaymentGateway $gateway,
private OrderWriter $repository,
private NotificationChannel $channel,
) {}
public function process(Order $order, string $notifyRecipient): PaymentResult
{
$this->validator->validate($order);
$discount = $this->discounts->totalDiscount($order);
$amountDue = $order->total() - $discount;
$result = $this->gateway->charge($amountDue);
$this->repository->save($order);
$this->channel->send(
$notifyRecipient,
"Order #{$order->id()} confirmed, charged {$result->chargedAmountMinor} minor units.",
);
return $result;
}
}
Wer diese Pipeline testen will, injiziert für jede der fünf Abhängigkeiten ein Test-Double, ein Fake-PaymentGateway, das immer erfolgreich abrechnet, ein Fake-NotificationChannel, das Nachrichten nur sammelt. Kein Test braucht eine echte Datenbankverbindung oder einen echten SMTP-Server. Genau diese Testbarkeit ist der praktische Beweis dafür, dass die SOLID-Prinzipien hier tatsächlich greifen, nicht nur auf dem Papier stehen.
8. Wann SOLID übertrieben ist: pragmatische Grenzen ziehen
Die SOLID-Prinzipien lassen sich auch übertreiben, und genau das passiert regelmäßig in Codebasen, die nie über den Prototyp-Status hinausgekommen sind. Ein internes Skript, das einmal im Monat eine CSV-Datei generiert, braucht kein DiscountRule-Interface mit drei Implementierungen, wenn es genau eine Rabattregel gibt und diese sich seit zwei Jahren nicht geändert hat. Ein Interface mit einer einzigen Implementierung, die niemals eine zweite bekommen wird, ist keine Anwendung der SOLID-Prinzipien, sondern reine Indirektion ohne Nutzen: mehr Dateien, mehr Sprünge beim Lesen, kein zusätzlicher Wert.
Der pragmatische Test lautet: Wird sich diese Stelle des Codes voraussichtlich in mehreren, fachlich unterschiedlichen Richtungen ändern? Bei der Order-Processing-Pipeline aus diesem Artikel ist die Antwort ja, neue Rabattaktionen, neue Zahlungsanbieter und neue Benachrichtigungskanäle kommen in jeder wachsenden E-Commerce-Anwendung realistisch vor. Bei einem internen Einmal-Skript ohne absehbare Variation ist die Antwort oft nein. Die SOLID-Prinzipien sind ein Mittel, um Änderungskosten in Code zu senken, der sich tatsächlich ändert. Wo diese Änderung nicht zu erwarten ist, produziert striktes Befolgen aller fünf Prinzipien nur zusätzliche Komplexität ohne Gegenwert, und pragmatisches Weglassen ist die richtige Entscheidung.
9. Verstoß vs. SOLID-konforme Lösung im direkten Vergleich
Die folgende Tabelle fasst alle fünf SOLID-Prinzipien anhand der Order-Processing-Pipeline zusammen: den typischen Verstoß, die konforme Lösung aus diesem Artikel und den konkreten Vorteil, den diese Lösung in der Praxis bringt.
| Prinzip | Typischer Verstoß | SOLID-konforme Lösung | Vorteil |
|---|---|---|---|
| Single Responsibility | Order validiert, speichert und versendet Mail |
Validator, Repository, Notifier getrennt | Isolierte, einfache Tests pro Verantwortung |
| Open/Closed | if/elseif-Kaskade für Rabattregeln | DiscountRule-Interface je Regel |
Neue Regel hinzufügen ohne Änderungsrisiko |
| Liskov Substitution | Subklasse gibt null statt Exception |
Alle Gateways halten denselben Vertrag ein | Sichere Austauschbarkeit ohne Sonderfälle |
| Interface Segregation | Ein fettes OrderRepositoryInterface |
Reader, Writer, Archiver getrennt | Clients hängen nur von benötigten Methoden ab |
| Dependency Inversion | Notifier hängt direkt von SmtpMailer ab |
NotificationChannel-Abstraktion |
Kanal wechseln ohne High-Level-Code zu ändern |
Der gemeinsame Nenner aller fünf Zeilen: Der Verstoß koppelt Code eng an ein konkretes Detail, das sich später ändert, während die SOLID-Prinzipien-konforme Lösung diese Änderung hinter einer Abstraktion isoliert. Genau diese Isolation ist es, die den Unterschied zwischen einem Bugfix von fünf Minuten und einer riskanten Änderung an drei Stellen ausmacht.
10. Zusammenfassung
Die SOLID-Prinzipien lösen an der Order-Processing-Pipeline aus diesem Artikel fünf verwandte, aber unterschiedliche Probleme. Single Responsibility trennt eine überladene Order-Klasse in vier fokussierte Klassen. Open/Closed ersetzt eine wachsende if/else-Kaskade durch austauschbare DiscountRule-Implementierungen. Liskov Substitution stellt sicher, dass jedes PaymentGateway denselben Vertrag einhält, egal welche konkrete Implementierung eingesetzt wird. Interface Segregation teilt ein fettes Repository-Interface in rollenspezifische Interfaces auf. Dependency Inversion entkoppelt den OrderShippedNotifier von einer konkreten Mailer-Klasse zugunsten einer Abstraktion.
Im Zusammenspiel ergeben die SOLID-Prinzipien eine OrderProcessor-Klasse, die reine Orchestrierung ist, isoliert testbar und ohne Kenntnis konkreter Implementierungsdetails. Wichtig bleibt der pragmatische Blick aus Abschnitt 8: Die SOLID-Prinzipien zahlen sich dort aus, wo Code sich tatsächlich in mehreren Richtungen weiterentwickelt, nicht überall automatisch. Wer diesen Unterschied erkennt, wendet die SOLID-Prinzipien gezielt an, statt sie mechanisch auf jede Klasse zu pressen.
SOLID-Prinzipien in der Praxis, das Wichtigste auf einen Blick
Single Responsibility
Eine Klasse, ein Änderungsgrund. Order, Validator, Repository und Notifier bleiben getrennt.
Open/Closed & Liskov Substitution
Neue Rabattregeln als eigene Klassen. Jede PaymentGateway-Implementierung hält denselben Vertrag ein.
Interface Segregation
Reader, Writer und Archiver getrennt statt ein fettes Repository-Interface.
Dependency Inversion
NotificationChannel-Abstraktion statt direkter Kopplung an SmtpMailer.
11. FAQ: SOLID-Prinzipien in der Praxis
1Was sind die SOLID-Prinzipien in einem Satz?
2Warum reichen Formen- oder Tier-Beispiele nicht aus?
3Was ist der Kern des Single Responsibility Principle?
4Wie vermeidet man if/else-Kaskaden bei Rabattregeln?
5Was bedeutet ein Liskov-Substitution-Verstoß konkret?
6Wie erkennt man ein zu fettes Interface?
7Was bedeutet Dependency Inversion in der Praxis?
8Kann man SOLID-Prinzipien übertreiben?
9Muss man alle fünf Prinzipien gleichzeitig anwenden?
10Sind SOLID-Prinzipien an ein Framework gebunden?
Mironsoft
Objektorientiertes Design, Code-Reviews und Architektur-Beratung
Codebasis ohne klare Verantwortlichkeiten und Abstraktionen?
Wir analysieren bestehenden PHP-Code, identifizieren Verstöße gegen die SOLID-Prinzipien und refaktorieren gezielt dort, wo es sich tatsächlich auszahlt, ohne Over-Engineering und ohne unnötige Interfaces.
Architektur-Review
Analyse bestehender Klassenstrukturen auf SOLID-Prinzipien-Verstöße und Kopplungsrisiken
Gezieltes Refactoring
Verantwortlichkeiten trennen, Interfaces schärfen, Abhängigkeiten umkehren, pragmatisch dosiert
Team-Schulung
SOLID-Prinzipien an echten Beispielen eures eigenen Codes statt an Theorie vermitteln