SOLID là gì?
SOLID gồm 5 nguyên tắc thiết kế hướng đối tượng:
- S – Single Responsibility Principle
- O – Open/Closed Principle
- L – Liskov Substitution Principle
- I – Interface Segregation Principle
- D – Dependency Inversion Principle
Mục tiêu: mỗi module có một trách nhiệm rõ ràng, dễ mở rộng mà không sửa code cũ, dễ test và dễ tái sử dụng.
1. Single Responsibility Principle (SRP)
Mỗi class chỉ nên có một lý do để thay đổi. Đừng nhồi nhiều chức năng vào một class.
# Sai: vừa quản lý user vừa gửi email
class UserService:
def create_user(self, user): ...
def send_welcome_email(self, user): ...
# Đúng: tách rõ trách nhiệm
class UserService:
def create_user(self, user): ...
class EmailService:
def send_welcome_email(self, user): ...
2. Open/Closed Principle (OCP)
Class nên mở rộng được nhưng không sửa đổi code gốc. Thay vì sửa class, hãy kế thừa hoặc dùng abstraction.
// Sai: phải sửa code nếu thêm loại shape mới
class AreaCalculator {
double calc(Object shape) {
if (shape instanceof Circle) ...
if (shape instanceof Square) ...
}
}
// Đúng: đóng với sửa đổi, mở với mở rộng
interface Shape {
double area();
}
class Circle implements Shape { ... }
class Square implements Shape { ... }
class AreaCalculator {
double calc(Shape shape) {
return shape.area();
}
}
3. Liskov Substitution Principle (LSP)
Class con có thể thay thế class cha mà không phá vỡ chương trình.
// Sai: class con vi phạm hành vi cha
class Bird {
void fly();
}
class Ostrich extends Bird {
void fly() { throw new UnsupportedOperationException(); }
}
// Đúng: thiết kế hierarchy hợp lý
interface Bird { }
interface Flyable extends Bird {
void fly();
}
class Sparrow implements Flyable { ... }
class Ostrich implements Bird { ... }
4. Interface Segregation Principle (ISP)
Đừng ép class implement những method không dùng. Chia nhỏ interface để cụ thể hơn.
// Sai: interface quá to
interface Machine {
start();
stop();
print();
scan();
}
class Printer implements Machine {
start() {}
stop() {}
print() {}
scan() { throw "Not supported"; }
}
// Đúng: chia nhỏ interface
interface Printer {
print();
}
interface Scanner {
scan();
}
5. Dependency Inversion Principle (DIP)
Code nên phụ thuộc vào abstraction thay vì implementation cụ thể để dễ test và thay thế.
// Sai: phụ thuộc trực tiếp vào EmailService
class UserController {
private EmailService emailService = new EmailService();
}
// Đúng: phụ thuộc abstraction
interface MessageService {
void send(String msg);
}
class EmailService implements MessageService { ... }
class UserController {
private MessageService service;
public UserController(MessageService s) {
this.service = s;
}
}
Demo trực quan: từ “cứng” sang “mềm” với OCP
Giả sử bạn viết app order pizza. Không áp dụng SOLID, code sẽ khó mở rộng:
# Version chưa SOLID
class PizzaOrder:
def process_order(self, pizza_type):
if pizza_type == "Margherita":
print("Making Margherita")
elif pizza_type == "Pepperoni":
print("Making Pepperoni")
Muốn thêm loại pizza mới phải sửa trực tiếp vào PizzaOrder → vi phạm OCP.
Áp dụng SOLID (abstraction + composition):
from abc import ABC, abstractmethod
class Pizza(ABC):
@abstractmethod
def prepare(self):
pass
class Margherita(Pizza):
def prepare(self):
print("Making Margherita")
class Pepperoni(Pizza):
def prepare(self):
print("Making Pepperoni")
class OrderProcessor:
def __init__(self, pizza: Pizza):
self.pizza = pizza
def process(self):
self.pizza.prepare()
# Demo
order = OrderProcessor(Margherita())
order.process() # Making Margherita
Khi thêm loại pizza mới, chỉ cần tạo class kế thừa Pizza, không phải chạm vào OrderProcessor. Dễ test, dễ mở rộng.
Kết luận
SOLID giúp code rõ ràng, dễ bảo trì, dễ mở rộng và dễ test. Khi mới bắt đầu, hãy tập thói quen: tách trách nhiệm nhỏ, thiết kế hướng abstraction, và luôn tự hỏi “thêm tính năng mới có cần sửa code cũ không?”. Đó là cốt lõi của clean code.
Bình luận