Thiết Kế Phần Mềm Theo Mô Hình OOP Trong Python
Lập trình hướng đối tượng (OOP) không chỉ giúp tổ chức mã nguồn gọn gàng mà còn đóng vai trò quan trọng trong thiết kế phần mềm. Khi áp dụng OOP vào dự án thực tế, chúng ta cần tuân theo các nguyên tắc và mô hình thiết kế chuẩn như:
- Nguyên tắc SOLID
- Mô hình MVC (Model-View-Controller)
- Các mẫu thiết kế phổ biến: Factory Pattern, Singleton Pattern, Observer Pattern,…
1. Nguyên Tắc SOLID Trong Python
SOLID là 5 nguyên tắc giúp viết mã dễ bảo trì, mở rộng và tránh lỗi.
1.1. Single Responsibility Principle (SRP) – Nguyên Tắc Trách Nhiệm Duy Nhất
Mỗi class chỉ nên có một lý do để thay đổi, tức là chỉ thực hiện một nhiệm vụ duy nhất.
❌ Không đúng: (Class làm quá nhiều việc)
class Report:
def __init__(self, data):
self.data = data
def generate_report(self):
return f"Báo cáo: {self.data}"
def save_to_file(self, filename):
with open(filename, "w") as f:
f.write(self.generate_report()) # Vi phạm SRP
✅ Đúng: (Tách thành 2 class riêng biệt)
class Report:
def __init__(self, data):
self.data = data
def generate_report(self):
return f"Báo cáo: {self.data}"
class ReportSaver:
def save_to_file(self, report, filename):
with open(filename, "w") as f:
f.write(report.generate_report())
💡 Lợi ích: Dễ bảo trì hơn khi thay đổi logic xử lý báo cáo hoặc lưu file.
1.2. Open/Closed Principle (OCP) – Mở Rộng Nhưng Không Sửa Đổi
Code nên mở rộng được nhưng không sửa đổi code cũ khi thêm tính năng mới.
❌ Không đúng: (Sửa đổi class cũ khi thêm phương thức mới)
class PaymentProcessor:
def pay(self, method, amount):
if method == "credit":
print(f"Thanh toán {amount} bằng thẻ tín dụng")
elif method == "paypal":
print(f"Thanh toán {amount} qua PayPal")
✅ Đúng: (Dùng kế thừa để mở rộng)
from abc import ABC, abstractmethod
class PaymentMethod(ABC):
@abstractmethod
def pay(self, amount):
pass
class CreditCardPayment(PaymentMethod):
def pay(self, amount):
print(f"Thanh toán {amount} bằng thẻ tín dụng")
class PayPalPayment(PaymentMethod):
def pay(self, amount):
print(f"Thanh toán {amount} qua PayPal")
def process_payment(payment: PaymentMethod, amount):
payment.pay(amount)
process_payment(CreditCardPayment(), 100) # Output: Thanh toán 100 bằng thẻ tín dụng
💡 Lợi ích: Thêm phương thức thanh toán mới mà không sửa đổi code cũ.
1.3. Liskov Substitution Principle (LSP) – Nguyên Tắc Thay Thế Liskov
Lớp con có thể thay thế lớp cha mà không làm thay đổi hành vi chương trình.
❌ Không đúng: (Lớp con thay đổi hành vi của lớp cha không hợp lý)
class Bird:
def fly(self):
return "Bay trên trời"
class Penguin(Bird):
def fly(self): # Vi phạm LSP vì chim cánh cụt không bay
raise Exception("Chim cánh cụt không thể bay")
✅ Đúng: (Dùng phân cấp hợp lý)
class Bird:
pass
class FlyingBird(Bird):
def fly(self):
return "Bay trên trời"
class Penguin(Bird):
def swim(self):
return "Bơi dưới nước"
💡 Lợi ích: Lớp con có thể thay thế lớp cha mà không gây lỗi.
1.4. Interface Segregation Principle (ISP) – Nguyên Tắc Phân Tách Giao Diện
Lớp không nên phụ thuộc vào phương thức mà nó không sử dụng.
❌ Không đúng: (Class phải triển khai phương thức không cần thiết)
class Worker:
def work(self):
pass
def eat(self):
pass # Không phải worker nào cũng cần phương thức này
✅ Đúng: (Tách thành nhiều interface nhỏ)
from abc import ABC, abstractmethod
class Workable(ABC):
@abstractmethod
def work(self):
pass
class Eatable(ABC):
@abstractmethod
def eat(self):
pass
class Robot(Workable):
def work(self):
print("Robot làm việc")
class Human(Workable, Eatable):
def work(self):
print("Con người làm việc")
def eat(self):
print("Con người ăn cơm")
💡 Lợi ích: Lớp chỉ triển khai phương thức cần thiết.
1.5. Dependency Inversion Principle (DIP) – Đảo Ngược Phụ Thuộc
Các module cấp cao không nên phụ thuộc vào module cấp thấp, cả hai nên phụ thuộc vào abstraction.
❌ Không đúng: (Lớp cấp cao phụ thuộc vào lớp cụ thể)
class MySQLDatabase:
def connect(self):
print("Kết nối MySQL")
class App:
def __init__(self):
self.db = MySQLDatabase() # Phụ thuộc vào lớp cụ thể
def start(self):
self.db.connect()
✅ Đúng: (Dùng interface để giảm phụ thuộc)
from abc import ABC, abstractmethod
class Database(ABC):
@abstractmethod
def connect(self):
pass
class MySQLDatabase(Database):
def connect(self):
print("Kết nối MySQL")
class PostgreSQLDatabase(Database):
def connect(self):
print("Kết nối PostgreSQL")
class App:
def __init__(self, db: Database):
self.db = db
def start(self):
self.db.connect()
app = App(MySQLDatabase()) # Có thể dễ dàng thay đổi DB mà không sửa code
app.start()
💡 Lợi ích: Code linh hoạt hơn khi thay đổi database.
2. Tổng Kết
✔️ SOLID giúp thiết kế phần mềm tốt hơn trong OOP Python:
✅ SRP: Một class chỉ nên có một trách nhiệm duy nhất.
✅ OCP: Class nên mở rộng thay vì sửa đổi.
✅ LSP: Lớp con có thể thay thế lớp cha mà không làm thay đổi chương trình.
✅ ISP: Class chỉ nên triển khai các phương thức cần thiết.
✅ DIP: Giảm phụ thuộc giữa các module bằng abstraction.
💡 Bạn có câu hỏi gì về SOLID? Hãy để lại bình luận bên dưới!