SOLID elvek
S — Single Responsibility Principle (Egyetlen felelősség elve)
Egy osztálynak csak egy oka legyen a változásra.
❌ Rossz példa
public class OrderService {
public void placeOrder(Order order) {
// üzleti logika
order.setStatus(OrderStatus.CONFIRMED);
// e-mail küldés — ez nem az OrderService dolga
String body = "Rendelés visszaigazolva: " + order.getId();
EmailClient.send(order.getCustomerEmail(), body);
// naplózás — ez sem
System.out.println("Order placed: " + order.getId());
}
}
✅ Jó példa
public class OrderService {
private final EmailNotifier emailNotifier;
private final OrderLogger orderLogger;
public void placeOrder(Order order) {
order.setStatus(OrderStatus.CONFIRMED);
emailNotifier.sendConfirmation(order);
orderLogger.log(order);
}
}
public class EmailNotifier {
public void sendConfirmation(Order order) {
String body = "Rendelés visszaigazolva: " + order.getId();
EmailClient.send(order.getCustomerEmail(), body);
}
}
public class OrderLogger {
public void log(Order order) {
System.out.println("Order placed: " + order.getId());
}
}
Miért jobb? Az OrderService csak az üzleti logikáért felelős. Ha az e-mail sablon változik, csak az EmailNotifier-t kell módosítani — az OrderService-t nem.
O — Open/Closed Principle (Nyitott/zárt elv)
Az osztályok legyenek nyitottak a bővítésre, de zártak a módosításra.
❌ Rossz példa
public class DiscountCalculator {
public double calculate(Order order, String customerType) {
if (customerType.equals("VIP")) {
return order.getTotal() * 0.80;
} else if (customerType.equals("STUDENT")) {
return order.getTotal() * 0.90;
}
return order.getTotal();
}
}
Új kedvezménytípus hozzáadásakor mindig módosítani kell ezt az osztályt.
✅ Jó példa
public interface DiscountPolicy {
double apply(double total);
}
public class VipDiscount implements DiscountPolicy {
public double apply(double total) { return total * 0.80; }
}
public class StudentDiscount implements DiscountPolicy {
public double apply(double total) { return total * 0.90; }
}
public class NoDiscount implements DiscountPolicy {
public double apply(double total) { return total; }
}
public class DiscountCalculator {
public double calculate(Order order, DiscountPolicy policy) {
return policy.apply(order.getTotal());
}
}
Miért jobb? Új kedvezménytípus hozzáadásához csak egy új implementációt kell írni — a DiscountCalculator érintetlen marad.
L — Liskov Substitution Principle (Liskov-féle helyettesítési elv)
Az alosztályok a szülőosztályuk helyett is felhasználhatók kell legyenek, a program helyes működése nélkül.
❌ Rossz példa
public class Rectangle {
protected int width, height;
public void setWidth(int width) { this.width = width; }
public void setHeight(int height) { this.height = height; }
public int area() { return width * height; }
}
public class Square extends Rectangle {
@Override
public void setWidth(int width) {
this.width = width;
this.height = width; // megszegi a Rectangle kontraktusát!
}
@Override
public void setHeight(int height) {
this.width = height;
this.height = height;
}
}
// Klienskód, ami Rectangle-t vár:
Rectangle r = new Square();
r.setWidth(5);
r.setHeight(3);
System.out.println(r.area()); // 9, nem 15 — meglepetés!
✅ Jó példa
public interface Shape {
int area();
}
public class Rectangle implements Shape {
private final int width, height;
public Rectangle(int width, int height) {
this.width = width;
this.height = height;
}
public int area() { return width * height; }
}
public class Square implements Shape {
private final int side;
public Square(int side) { this.side = side; }
public int area() { return side * side; }
}
Miért jobb? A Square nem örökli a Rectangle viselkedési kontraktusát — mindkét forma önállóan, helyesen viselkedik.
I — Interface Segregation Principle (Interfész szegregáció elve)
Egy osztályt ne kényszerítsünk olyan metódusok implementálására, amelyeket nem használ.
❌ Rossz példa
public interface Worker {
void work();
void eat();
void sleep();
}
public class Robot implements Worker {
public void work() { /* rendben */ }
public void eat() { throw new UnsupportedOperationException(); } // robot nem eszik
public void sleep(){ throw new UnsupportedOperationException(); } // robot nem alszik
}
✅ Jó példa
public interface Workable {
void work();
}
public interface Restable {
void eat();
void sleep();
}
public class HumanWorker implements Workable, Restable {
public void work() { System.out.println("Dolgozik."); }
public void eat() { System.out.println("Eszik."); }
public void sleep() { System.out.println("Alszik."); }
}
public class Robot implements Workable {
public void work() { System.out.println("Robot dolgozik."); }
}
Miért jobb? A Robot csak azt az interfészt implementálja, ami rá vonatkozik. Nincs felesleges kód, nincs UnsupportedOperationException.
D — Dependency Inversion Principle (Függőség megfordításának elve)
A magas szintű modulok ne függjenek alacsony szintű moduloktól. Mindkettő absztrakciótól függjön.
❌ Rossz példa
public class OrderService {
private final MySQLOrderRepository repository = new MySQLOrderRepository(); // konkrét implementáció
public void save(Order order) {
repository.save(order);
}
}
Ha PostgreSQL-re váltunk, az OrderService-t kell módosítani.
✅ Jó példa
public interface OrderRepository {
void save(Order order);
Optional<Order> findById(Long id);
}
public class MySQLOrderRepository implements OrderRepository {
public void save(Order order) { /* MySQL-specifikus kód */ }
public Optional<Order> findById(Long id) { /* ... */ return Optional.empty(); }
}
public class OrderService {
private final OrderRepository repository; // absztrakció
public OrderService(OrderRepository repository) { // DI konstruktoron keresztül
this.repository = repository;
}
public void save(Order order) {
repository.save(order);
}
}
Spring kontextusban:
@Service
public class OrderService {
private final OrderRepository repository;
public OrderService(OrderRepository repository) {
this.repository = repository;
}
}
@Repository
public class MySQLOrderRepository implements OrderRepository { ... }
Miért jobb? Az OrderService nem tudja (és nem is kell tudnia), hogy MySQL vagy PostgreSQL van mögötte. Teszteléskor könnyen cserélhető mock implementációra.
Összefoglalás
| Elv | Lényeg | Kulcsszó |
|---|---|---|
| SRP | Egy osztály = egy felelősség | Cohesion |
| OCP | Bővíthető, de nem módosítandó | Abstraction |
| LSP | Alosztály behelyettesíthető | Contracts |
| ISP | Vékony, célzott interfészek | Segregation |
| DIP | Absztrakcióra támaszkodj, ne konkrét osztályra | Inversion |