Explore all courses in this category.
Explore all courses in this category.
Nguyễn Đình Cường
0 comment
09 Nov, 2026
Bạn cắm đủ loại thiết bị vào ổ điện mà không cần quan tâm bên trong có gì. Ổ cắm chỉ đặt ra một chuẩn chung. Đó chính là tinh thần của đa hình và trừu tượng trong lập trình: làm việc với "giao diện" thay vì chi tiết.
Đa hình (polymorphism) nghĩa là "nhiều hình thái": cùng một lời gọi nhưng hành vi khác nhau tùy đối tượng thực sự nhận lời gọi. Ví dụ, mọi phương thức thanh toán đều có thao tác thanh_toan(so_tien) nhưng cách làm của thẻ, ví điện tử, tiền mặt hoàn toàn khác.
class ThanhToan:
def thanh_toan(self, so_tien):
raise NotImplementedError
class TheNganHang(ThanhToan):
def thanh_toan(self, so_tien):
return f"Trừ {so_tien} từ thẻ"
class TienMat(ThanhToan):
def thanh_toan(self, so_tien):
return f"Nhận {so_tien} tiền mặt"
def thu_tien(phuong_thuc, so_tien):
return phuong_thuc.thanh_toan(so_tien)
print(thu_tien(TheNganHang(), 100000))
print(thu_tien(TienMat(), 50000))Hàm thu_tien không cần biết phương thức cụ thể là gì. Khi thêm hình thức mới, bạn viết thêm một lớp mà không sửa thu_tien. Đây chính là lợi ích lớn nhất: dễ mở rộng, ít sửa code cũ.
Trừu tượng (abstraction) là chỉ nêu những gì cần biết để sử dụng, giấu phần còn lại. Bạn dùng len(ds) mà không cần biết danh sách lưu độ dài thế nào. Trong thiết kế lớp, ta thể hiện sự trừu tượng bằng hai công cụ.
Lớp không thể tạo object trực tiếp, vừa có thể chứa phương thức đã cài đặt sẵn, vừa có phương thức trừu tượng buộc lớp con phải cài:
from abc import ABC, abstractmethod
class Hinh(ABC):
@abstractmethod
def dien_tich(self):
pass
def mo_ta(self):
return f"Diện tích: {self.dien_tich()}"
class Vuong(Hinh):
def __init__(self, a):
self.a = a
def dien_tich(self):
return self.a ** 2
print(Vuong(4).mo_ta()) # Diện tích: 16
# Hinh() -> TypeError: không tạo được lớp trừu tượngInterface chỉ khai báo "hợp đồng": những phương thức cần có, không có phần thân. Java, C#, TypeScript hỗ trợ interface trực tiếp. Một lớp có thể cài đặt nhiều interface, trong khi thường chỉ kế thừa được một lớp cha. Ví dụ TypeScript:
interface CoTheBay {
bay(): string;
}
class ChimSao implements CoTheBay {
bay() { return "Vỗ cánh"; }
}| Tình huống | Chọn |
|---|---|
| Các lớp con chia sẻ code chung và trạng thái | Abstract class |
| Chỉ cần quy định hành vi, các lớp không liên quan nhau | Interface |
| Cần một lớp cài nhiều "vai trò" | Interface |
Python dùng "duck typing": nếu một đối tượng có phương thức cần thiết thì dùng được, không cần khai báo interface; có câu nói vui rằng nếu nó đi như vịt và kêu như vịt thì ta coi nó là vịt.
Hãy kiểm tra sức mạnh của đa hình bằng thiết kế một bộ gửi thông báo qua nhiều kênh:
class KenhThongBao:
def gui(self, nguoi_nhan, noi_dung):
raise NotImplementedError
class Email(KenhThongBao):
def gui(self, nguoi_nhan, noi_dung):
print(f"[Email] gửi tới {nguoi_nhan}: {noi_dung}")
class Sms(KenhThongBao):
def gui(self, nguoi_nhan, noi_dung):
print(f"[SMS] gửi tới {nguoi_nhan}: {noi_dung}")
def thong_bao_tat_ca(kenh_ds, nguoi_nhan, noi_dung):
for kenh in kenh_ds:
kenh.gui(nguoi_nhan, noi_dung)
thong_bao_tat_ca([Email(), Sms()], "lan", "Khóa học mới")Muốn thêm kênh push, bạn chỉ viết thêm một lớp có phương thức gui. Hàm thong_bao_tat_ca không cần sửa. Đây là nguyên tắc "mở để mở rộng, đóng để sửa đổi". Khi kiểm thử, bạn có thể tạo một kênh giả chỉ ghi lại các tin nhắn để kiểm tra, mà không phải gửi thật.
if loai == "email" rải khắp nơi: dấu hiệu bạn nên dùng đa hình.Đa hình cho phép đối xử với nhiều loại đối tượng theo cùng một cách, còn trừu tượng giúp ta tập trung vào giao diện thay vì chi tiết. Hãy luyện bằng cách thiết kế một hệ thống thông báo có email, SMS, push cùng chung một giao diện, rồi thêm kênh mới mà không đụng đến code cũ.
Khi đã sẵn sàng đi sâu hơn, bạn có thể tham khảo lộ trình học trong các khóa học tại codebem.com.
Nguyễn Đình Cường
0 comment