SOLID-principerna
Designmönster är kraftfulla, men de vilar på en grund av välstrukturerad objektorienterad design. De mest kända principerna för detta är SOLID — fem riktlinjer som gör mjukvara mer förståelig, flexibel och underhållbar.
Vad är SOLID?
SOLID är en akronym för fem designprinciper formulerade av Robert C. Martin (“Uncle Bob”). Varje princip tar itu med ett specifikt designproblem som annars gör koden skör och svårföränderlig.
S — Single Responsibility Principle
“En klass ska bara ha en anledning att ändras.”
En klass ska ha ett enda, väldefinierat ansvar. Om en klass gör för många saker — hanterar affärslogik, formaterar data och skriver till databasen — blir den svår att förstå och riskerar att gå sönder när en del ändras.
Dåligt exempel:
// Klassen gör för mycket — sparar OCH formaterar OCH skickar e-post
public class Order {
public void saveToDatabase() { /* ... */ }
public String formatAsInvoice() { /* ... */ }
public void sendConfirmationEmail() { /* ... */ }
}
Rätt approach: Dela upp i OrderRepository, InvoiceFormatter och EmailService.
O — Open/Closed Principle
“Mjukvaruentiteter ska vara öppna för utökning, men stängda för modifiering.”
Du ska kunna lägga till ny funktionalitet utan att ändra i befintlig, testad kod. Detta uppnås med gränssnitt (interfaces) och polymorfism.
Exempel: Istället för att lägga till en ny if-gren varje gång en ny betalningsmetod tillkommer, skapa ett PaymentStrategy-gränssnitt som nya klasser implementerar.
public interface PaymentStrategy {
void pay(int amount);
}
// Ny betalningsmetod = ny klass, ingen ändring i befintlig kod
public class SwishPayment implements PaymentStrategy {
public void pay(int amount) { System.out.println("Paying " + amount + " via Swish"); }
}
L — Liskov Substitution Principle
“Objekt av en superklass ska kunna ersättas med objekt av dess underklasser utan att applikationen går sönder.”
En underklass ska uppfylla alla förväntningar som ställs på superklassen. Om du har kod som fungerar med en Bird, ska den också fungera med en Eagle (underklass). En Penguin bryter mot LSP om den ärver en fly()-metod som kastar ett undantag.
// Bryter mot LSP — Penguin kan inte flyga men tvingas implementera fly()
public class Penguin extends Bird {
@Override
public void fly() {
throw new UnsupportedOperationException("Penguins can't fly!");
}
}
Lösning: Skapa separata gränssnitt FlyingBird och SwimmingBird.
I — Interface Segregation Principle
“Många specifika gränssnitt är bättre än ett brett gränssnitt.”
Klienter ska inte tvingas implementera metoder de inte använder. Dela upp stora “feta” gränssnitt i mindre, mer specifika sådana.
// Dåligt: ett brett gränssnitt som inte alla kan implementera
public interface Worker {
void work();
void eat(); // En robot behöver inte äta!
void sleep(); // En robot behöver inte sova!
}
// Rätt: separerade gränssnitt
public interface Workable { void work(); }
public interface Feedable { void eat(); }
public interface Restable { void sleep(); }
D — Dependency Inversion Principle
“Bero på abstraktioner, inte på konkretioner.”
Högnivåmoduler ska inte bero direkt på lågnivåmoduler. Båda ska bero på abstraktioner (gränssnitt). Detta är grunden för Dependency Injection.
// Dåligt: OrderService är hårt kopplad till MySQLDatabase
public class OrderService {
private MySQLDatabase db = new MySQLDatabase(); // Konkret klass!
}
// Rätt: beroende på ett gränssnitt, injicerat utifrån
public class OrderService {
private final Database db; // Abstraktion
public OrderService(Database db) { // Injiceras
this.db = db;
}
}
Hur hänger SOLID ihop med Designmönster?
Många designmönster implementerar en eller flera av dessa principer direkt:
| Mönster | SOLID-princip |
|---|---|
| Strategy | Open/Closed — lägg till algoritmer utan att ändra befintlig kod |
| Facade | Single Responsibility — separerar och isolerar komplexa delsystem |
| Factory | Dependency Inversion — döljer konkreta klasser bakom ett gränssnitt |
| Observer | Open/Closed — lägg till nya prenumeranter utan att ändra publishern |
| Adapter | Single Responsibility — isolerar konverteringslogiken i en egen klass |
Av Victor Hernandez från Bytebase.se