Khái Niệm Cơ Bản Về Lớp Trừu Tượng
Nếu như các class thông thường mô tả cấu trúc của một nhóm đối tượng cụ thể với các thuộc tính và hành vi chung, thì Abstract Class (lớp trừu tượng) lại đóng vai trò là khuôn mẫu cho các class khác. Nó tập hợp những đặc điểm cốt lõi mà các lớp con bắt buộc phải tuân thủ.
Các Đặc Điểm Kỹ Thuật Chính
- Abstract class luôn cần chứa ít nhất một phương thức chưa được triển khai cụ thể (abstract method), tuy nhiên nó vẫn có thể chứa các phương thức bình thường đã có định nghĩa.
- Bên trong abstract class, các abstract method chỉ khai báo chữ ký mà không có thân hàm thực thi cụ thể.
- Về mặt ngữ nghĩa, bạn không thể tạo ra một thể hiện (instance) trực tiếp từ abstract class này.
- Quan trọng nhất: Khi viết lớp con (subclass) kế thừa từ abstract class, chương trình sẽ yêu cầu lớp con phải định nghĩa đầy đủ tất cả các phương thức trừu tượng trước khi cho phép khởi tạo đối tượng.
Ví Dụ Về Class Thông Thường
Xét trường hợp dưới đây, ta định nghĩa một lớp cha tiêu chuẩn không sử dụng thư viện hỗ trợ trừu tượng:
# -*- coding: utf-8 -*-
class BaseUser(object):
"""Lớp cha mặc định không có cơ chế ép buộc"""
def __init__(self, user_role, full_name, access_level):
self.user_role = user_role
self.full_name = full_name
self.access_level = access_level
# Phương thức đã được định nghĩa sẵn
def execute_command(self):
print("Executing default command...")
# Phương thức rỗng, không bắt buộc phải override
def custom_behavior(self):
pass
# Lớp con kế thừa
class AdminUser(BaseUser):
# Ghi đè phương thức kiểm tra quyền
def check_permission(self):
print("Admin Access Granted")
# Khởi tạo đối tượng
user_instance = AdminUser(user_role="admin", full_name="Nguyen Van A", access_level=5)
user_instance.execute_command()
Như bạn thấy, dù lớp cha BaseUser có một phương thức custom_behavior chưa làm gì cả (chỉ pass), nhưng việc tạo instance từ lớp con AdminUser vẫn diễn ra bình thường. Đây là cách hoạt động của class thường, không áp đặt quy tắc ép buộc.
Ví Dụ Về Abstract Class Gây Lỗi
Khi sử dụng module abc, chúng ta chuyển sang cơ chế ép buộc hợp đồng giao diện:
# -*- coding: utf-8 -*-
from abc import ABC, abstractmethod
class BaseSystem(ABC):
"""Abstract class yêu cầu kế thừa nghiêm ngặt"""
def __init__(self, system_id, config_map):
self.system_id = system_id
self.config_map = config_map
# Phương thức thường
def log_info(self):
print("Logging system info...")
# Phương thức trừu tượng bắt buộc
@abstractmethod
def initialize_module(self):
pass
# Lớp con bỏ sót phương thức abstract
class TestSystem(BaseSystem):
def log_info(self):
print("Custom logging enabled")
# Sẽ phát sinh lỗi khi cố gắng tạo instance
try:
sys_obj = TestSystem(system_id="T01", config_map={"debug": True})
except TypeError as err:
print(f"Lỗi xảy ra: {err}")
Đoạn mã trên sẽ dừng lại ở dòng khởi tạo đối tượng. Lý do là lớp TestSystem chưa cung cấp cài đặt cụ thể cho phương thức initialize_module được đánh dấu bởi @abstractmethod. Python không cho phép biến đổi lớp trừu tượng thành đối tượng sống nếu giao diện chưa hoàn thiện.
Thực Hiện Đúng Quy Trình
Để khắc phục vấn đề trên và hoàn thành giao diện lớp con, ta cần định nghĩa lại các phương thức trừu tượng:
# -*- coding: utf-8 -*-
from abc import ABC, abstractmethod
class BaseSystem(ABC):
def __init__(self, system_id, config_map):
self.system_id = system_id
self.config_map = config_map
def log_info(self):
print("Logging system info...")
@abstractmethod
def initialize_module(self):
pass
class ProductionSystem(BaseSystem):
# Ghi đè phương thức thông thường
def log_info(self):
print("Production mode logging active.")
# Bắt buộc phải định nghĩa để thỏa mãn điều kiện trừu tượng
def initialize_module(self):
print(f"Module initialized for ID: {self.system_id}")
# Khởi tạo thành công
sys_prod = ProductionSystem(system_id="P99", config_map={"secure": True})
sys_prod.log_info() # Production mode logging active.
sys_prod.initialize_module() # Module initialized for ID: P99