Bốn Trụ cột của Lập trình Hướng Đối tượng (OOP)
Bốn Trụ cột của Lập trình Hướng Đối tượng (OOP)
Lập trình hướng đối tượng dựa trên bốn nguyên lý cơ bản, thường được gọi là "bốn trụ cột" của OOP. Những nguyên lý này định hình cách chúng ta thiết kế và triển khai các chương trình hướng đối tượng. Trong Python, bốn trụ cột này được thể hiện với cú pháp đơn giản và linh hoạt, giúp lập trình viên dễ dàng áp dụng chúng vào thực tế.
1. Tính đóng gói (Encapsulation)
Định nghĩa và nguyên lý
Tính đóng gói là khả năng "đóng gói" dữ liệu (thuộc tính) và các phương thức xử lý dữ liệu đó vào trong một đơn vị duy nhất - đối tượng. Nguyên lý này còn bao gồm việc kiểm soát quyền truy cập vào các thành phần bên trong đối tượng, giúp bảo vệ dữ liệu khỏi sự truy cập trực tiếp từ bên ngoài.
Tính đóng gói giúp:
- Bảo vệ dữ liệu khỏi sự truy cập và sửa đổi không mong muốn
- Giảm sự phức tạp bằng cách ẩn các chi tiết triển khai
- Tăng tính bảo trì bằng cách cho phép thay đổi triển khai bên trong mà không ảnh hưởng đến mã bên ngoài
Cách thực hiện trong Python
Trong Python, tính đóng gói được thực hiện thông qua các quy ước đặt tên và các thuộc tính đặc biệt:
- Thuộc tính private: Sử dụng tiền tố
__
(hai dấu gạch dưới) để đánh dấu một thuộc tính là private - Thuộc tính protected: Sử dụng tiền tố
_
(một dấu gạch dưới) để đánh dấu một thuộc tính là protected - Getter và Setter: Sử dụng các phương thức hoặc thuộc tính để truy cập và sửa đổi dữ liệu
class BankAccount:
def __init__(self, account_number, balance):
self.__account_number = account_number # Thuộc tính private
self.__balance = balance # Thuộc tính private
# Getter cho account_number
@property
def account_number(self):
return self.__account_number
# Getter cho balance
@property
def balance(self):
return self.__balance
# Setter cho balance với kiểm tra điều kiện
@balance.setter
def balance(self, amount):
if amount < 0:
print("Số dư không thể âm!")
return
self.__balance = amount
# Phương thức công khai để thao tác với dữ liệu
def deposit(self, amount):
if amount > 0:
self.__balance += amount
print(f"Đã nạp {amount}đ. Số dư hiện tại: {self.__balance}đ")
else:
print("Số tiền nạp phải lớn hơn 0!")
def withdraw(self, amount):
if 0 < amount <= self.__balance:
self.__balance -= amount
print(f"Đã rút {amount}đ. Số dư hiện tại: {self.__balance}đ")
else:
print("Số tiền không hợp lệ hoặc vượt quá số dư!")
# Sử dụng lớp BankAccount
account = BankAccount("123456789", 1000000)
# Truy cập thông qua getter
print(f"Số tài khoản: {account.account_number}")
print(f"Số dư: {account.balance}đ")
# Sử dụng setter
account.balance = 1500000
print(f"Số dư mới: {account.balance}đ")
# Thử đặt số dư âm
account.balance = -500 # Sẽ hiển thị thông báo lỗi
# Sử dụng các phương thức công khai
account.deposit(500000)
account.withdraw(200000)
# Thử truy cập trực tiếp vào thuộc tính private
try:
print(account.__balance) # Sẽ gây ra lỗi AttributeError
except AttributeError as e:
print(f"Lỗi: {e}")
Lưu ý rằng trong Python, tính đóng gói không nghiêm ngặt như trong các ngôn ngữ khác như Java hoặc C++. Các thuộc tính private trong Python không thực sự "private" theo nghĩa tuyệt đối, mà chỉ là một quy ước đặt tên. Chúng vẫn có thể được truy cập thông qua tên đã được "name mangling" (_ClassName__attribute_name
).
# Truy cập thuộc tính private thông qua name mangling
print(account._BankAccount__balance) # Có thể truy cập, nhưng không nên làm như vậy
Ví dụ thực tế: Hệ thống quản lý nhân viên
class Employee:
def __init__(self, name, employee_id, salary):
self.name = name # Thuộc tính public
self._employee_id = employee_id # Thuộc tính protected
self.__salary = salary # Thuộc tính private
@property
def employee_id(self):
return self._employee_id
@property
def salary(self):
return self.__salary
@salary.setter
def salary(self, new_salary):
if new_salary > 0:
self.__salary = new_salary
else:
print("Lương phải lớn hơn 0!")
def display_info(self):
return f"Nhân viên: {self.name}, ID: {self._employee_id}, Lương: {self.__salary}đ"
class Manager(Employee):
def __init__(self, name, employee_id, salary, department):
super().__init__(name, employee_id, salary)
self.department = department
def display_info(self):
# Có thể truy cập thuộc tính protected _employee_id từ lớp cha
base_info = f"Quản lý: {self.name}, ID: {self._employee_id}"
# Không thể truy cập trực tiếp __salary từ lớp cha
return f"{base_info}, Phòng ban: {self.department}, Lương: {self.salary}đ"
# Tạo đối tượng
employee = Employee("Nguyễn Văn A", "E001", 10000000)
manager = Manager("Trần Thị B", "M001", 20000000, "Kỹ thuật")
# Hiển thị thông tin
print(employee.display_info())
print(manager.display_info())
# Thay đổi lương
employee.salary = 12000000
print(employee.display_info())
# Thử đặt lương âm
employee.salary = -5000000 # Sẽ hiển thị thông báo lỗi
2. Tính kế thừa (Inheritance)
Định nghĩa và nguyên lý
Tính kế thừa cho phép một lớp (lớp con) kế thừa các thuộc tính và phương thức từ một lớp khác (lớp cha). Điều này tạo ra mối quan hệ "is-a" giữa các lớp, ví dụ: một Manager "is-a" Employee.
Tính kế thừa giúp:
- Tái sử dụng mã nguồn
- Mở rộng chức năng của các lớp có sẵn
- Tạo ra cấu trúc phân cấp giữa các lớp
- Giảm sự trùng lặp mã nguồn
Cách thực hiện trong Python
Python hỗ trợ cả đơn kế thừa và đa kế thừa:
Đơn kế thừa
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
def make_sound(self):
return "Some generic animal sound"
def info(self):
return f"{self.name} is {self.age} years old"
class Dog(Animal):
def __init__(self, name, age, breed):
super().__init__(name, age) # Gọi constructor của lớp cha
self.breed = breed
def make_sound(self): # Ghi đè phương thức của lớp cha
return "Woof!"
def info(self):
base_info = super().info() # Gọi phương thức của lớp cha
return f"{base_info} and is a {self.breed}"
# Tạo đối tượng
animal = Animal("Generic Animal", 5)
dog = Dog("Buddy", 3, "Golden Retriever")
# Sử dụng các phương thức
print(animal.info()) # Generic Animal is 5 years old
print(animal.make_sound()) # Some generic animal sound
print(dog.info()) # Buddy is 3 years old and is a Golden Retriever
print(dog.make_sound()) # Woof!
Đa kế thừa
class Flyable:
def fly(self):
return "I can fly!"
class Swimmable:
def swim(self):
return "I can swim!"
class Duck(Animal, Flyable, Swimmable):
def __init__(self, name, age):
Animal.__init__(self, name, age)
def make_sound(self):
return "Quack!"
# Tạo đối tượng
duck = Duck("Donald", 2)
# Sử dụng các phương thức từ nhiều lớp cha
print(duck.info()) # Donald is 2 years old
print(duck.make_sound()) # Quack!
print(duck.fly()) # I can fly!
print(duck.swim()) # I can swim!
Method Resolution Order (MRO)
Khi sử dụng đa kế thừa, Python sử dụng thuật toán C3 để xác định thứ tự tìm kiếm phương thức (Method Resolution Order - MRO). MRO xác định thứ tự các lớp được tìm kiếm khi một phương thức được gọi.
class A:
def method(self):
return "Method from A"
class B(A):
def method(self):
return "Method from B"
class C(A):
def method(self):
return "Method from C"
class D(B, C):
pass
# Kiểm tra MRO
print(D.__mro__) # (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
# Tạo đối tượng và gọi phương thức
d = D()
print(d.method()) # Method from B (vì B đứng trước C trong MRO)
Ví dụ thực tế: Hệ thống phân cấp hình học
import math
class Shape:
def area(self):
pass
def perimeter(self):
pass
def description(self):
return "Đây là một hình cơ bản"
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return math.pi * self.radius ** 2
def perimeter(self):
return 2 * math.pi * self.radius
def description(self):
return f"Đây là hình tròn với bán kính {self.radius}"
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return 2 * (self.width + self.height)
def description(self):
return f"Đây là hình chữ nhật với chiều rộng {self.width} và chiều cao {self.height}"
class Square(Rectangle):
def __init__(self, side):
super().__init__(side, side)
def description(self):
return f"Đây là hình vuông với cạnh {self.width}"
# Tạo đối tượng
circle = Circle(5)
rectangle = Rectangle(4, 6)
square = Square(4)
# Sử dụng các phương thức
shapes = [circle, rectangle, square]
for shape in shapes:
print(shape.description())
print(f"Diện tích: {shape.area()}")
print(f"Chu vi: {shape.perimeter()}")
print()
3. Tính đa hình (Polymorphism)
Định nghĩa và nguyên lý
Tính đa hình cho phép các đối tượng của các lớp khác nhau được xử lý thông qua một giao diện chung. Điều này cho phép một phương thức hoạt động khác nhau tùy thuộc vào đối tượng mà nó được gọi.
Tính đa hình giúp:
- Tạo mã nguồn linh hoạt và có thể mở rộng
- Giảm sự phụ thuộc vào các kiểu cụ thể
- Đơn giản hóa mã nguồn bằng cách xử lý các đối tượng khác nhau một cách thống nhất
Cách thực hiện trong Python
Python hỗ trợ tính đa hình thông qua:
Ghi đè phương thức (Method Overriding)
class Animal:
def speak(self):
return "Animal makes a sound"
class Dog(Animal):
def speak(self):
return "Dog barks"
class Cat(Animal):
def speak(self):
return "Cat meows"
class Duck(Animal):
def speak(self):
return "Duck quacks"
# Hàm sử dụng tính đa hình
def animal_sound(animal):
return animal.speak()
# Tạo đối tượng
dog = Dog()
cat = Cat()
duck = Duck()
# Sử dụng tính đa hình
animals = [dog, cat, duck]
for animal in animals:
print(animal_sound(animal))
Duck Typing
Python sử dụng "duck typing" - một khái niệm trong đó kiểu của một đối tượng được xác định bởi hành vi của nó (các phương thức và thuộc tính) chứ không phải bởi kiểu khai báo.
class Car:
def move(self):
return "Car is driving on the road"
class Boat:
def move(self):
return "Boat is sailing on the water"
class Airplane:
def move(self):
return "Airplane is flying in the sky"
# Hàm sử dụng duck typing
def start_journey(vehicle):
# Không quan tâm vehicle thuộc lớp nào, chỉ cần có phương thức move()
return vehicle.move()
# Tạo đối tượng
car = Car()
boat = Boat()
airplane = Airplane()
# Sử dụng duck typing
vehicles = [car, boat, airplane]
for vehicle in vehicles:
print(start_journey(vehicle))
Ví dụ thực tế: Hệ thống thanh toán
class PaymentMethod:
def process_payment(self, amount):
pass
class CreditCard(PaymentMethod):
def __init__(self, card_number, expiry_date, cvv):
self.card_number = card_number
self.expiry_date = expiry_date
self.cvv = cvv
def process_payment(self, amount):
# Trong thực tế, đây sẽ là mã xử lý thanh toán thẻ tín dụng
last_four_digits = self.card_number[-4:]
return f"Đã thanh toán {amount}đ bằng thẻ tín dụng (****{last_four_digits})"
class PayPal(PaymentMethod):
def __init__(self, email):
self.email = email
def process_payment(self, amount):
# Trong thực tế, đây sẽ là mã xử lý thanh toán PayPal
return f"Đã thanh toán {amount}đ bằng PayPal ({self.email})"
class BankTransfer(PaymentMethod):
def __init__(self, account_number, bank_name):
self.account_number = account_number
self.bank_name = bank_name
def process_payment(self, amount):
# Trong thực tế, đây sẽ là mã xử lý chuyển khoản ngân hàng
return f"Đã thanh toán {amount}đ bằng chuyển khoản ngân hàng ({self.bank_name})"
class Order:
def __init__(self, items, total_amount):
self.items = items
self.total_amount = total_amount
def checkout(self, payment_method):
# Không quan tâm payment_method thuộc lớp nào, chỉ cần có phương thức process_payment()
return payment_method.process_payment(self.total_amount)
# Tạo đối tượng thanh toán
credit_card = CreditCard("1234567890123456", "12/25", "123")
paypal = PayPal("example@email.com")
bank_transfer = BankTransfer("9876543210", "VietcomBank")
# Tạo đơn hàng
order = Order(["Laptop", "Mouse", "Keyboard"], 25000000)
# Thanh toán bằng các phương thức khác nhau
print(order.checkout(credit_card))
print(order.checkout(paypal))
print(order.checkout(bank_transfer))
4. Tính trừu tượng (Abstraction)
Định nghĩa và nguyên lý
Tính trừu tượng là khả năng tập trung vào những khía cạnh thiết yếu của một đối tượng mà không cần quan tâm đến các chi tiết triển khai cụ thể. Nó cho phép chúng ta xác định giao diện chung cho một nhóm các lớp liên quan.
Tính trừu tượng giúp:
- Giảm sự phức tạp bằng cách ẩn các chi tiết triển khai
- Tạo ra các giao diện rõ ràng và dễ sử dụng
- Tăng tính bảo trì và mở rộng của mã nguồn
Cách thực hiện trong Python
Python hỗ trợ tính trừu tượng thông qua module abc
(Abstract Base Classes):
from abc import ABC, abstractmethod
class AbstractVehicle(ABC):
@abstractmethod
def start_engine(self):
pass
@abstractmethod
def stop_engine(self):
pass
def honk(self):
return "Beep beep!"
class Car(AbstractVehicle):
def start_engine(self):
return "Car engine started"
def stop_engine(self):
return "Car engine stopped"
class Motorcycle(AbstractVehicle):
def start_engine(self):
return "Motorcycle engine started"
def stop_engine(self):
return "Motorcycle engine stopped"
def honk(self):
return "Beep!"
# Không thể tạo đối tượng từ lớp trừu tượng
try:
vehicle = AbstractVehicle()
except TypeError as e:
print(f"Lỗi: {e}")
# Tạo đối tượng từ các lớp con
car = Car()
motorcycle = Motorcycle()
# Sử dụng các phương thức
print(car.start_engine())
print(car.honk())
print(car.stop_engine())
print(motorcycle.start_engine())
print(motorcycle.honk())
print(motorcycle.stop_engine())
Ví dụ thực tế: Hệ thống xử lý dữ liệu
from abc import ABC, abstractmethod
class DataProcessor(ABC):
@abstractmethod
def process_data(self, data):
pass
@abstractmethod
def validate_data(self, data):
pass
def log_process(self, message):
print(f"Log: {message}")
class TextProcessor(DataProcessor):
def validate_data(self, data):
if not isinstance(data, str):
raise ValueError("Data must be a string")
return True
def process_data(self, data):
self.validate_data(data)
# Xử lý văn bản
result = data.upper()
self.log_process(f"Processed text data: {data[:20]}...")
return result
class NumberProcessor(DataProcessor):
def validate_data(self, data):
if not isinstance(data, (int, float, list)):
raise ValueError("Data must be a number or list of numbers")
return True
def process_data(self, data):
self.validate_data(data)
# Xử lý dữ liệu số
if isinstance(data, (int, float)):
result = data * 2
self.log_process(f"Processed numeric data: {data} → {result}")
return result
elif isinstance(data, list):
result = [x * 2 for x in data if isinstance(x, (int, float))]
self.log_process(f"Processed list of numbers with {len(result)} items")
return result
class ImageProcessor(DataProcessor):
def validate_data(self, data):
# Giả sử data là một đối tượng hình ảnh
if not hasattr(data, 'width') or not hasattr(data, 'height'):
raise ValueError("Data must have width and height attributes")
return True
def process_data(self, data):
self.validate_data(data)
# Xử lý hình ảnh
self.log_process(f"Processed image data: {data.width}x{data.height}")
return f"Processed image: {data.width}x{data.height}"
# Tạo đối tượng dữ liệu giả định
class ImageData:
def __init__(self, width, height):
self.width = width
self.height = height
# Tạo các bộ xử lý
text_processor = TextProcessor()
number_processor = NumberProcessor()
image_processor = ImageProcessor()
# Xử lý dữ liệu
try:
print(text_processor.process_data("hello world"))
print(number_processor.process_data(42))
print(number_processor.process_data([1, 2, 3, 4, 5]))
print(image_processor.process_data(ImageData(800, 600)))
except ValueError as e:
print(f"Lỗi: {e}")
Kết hợp cả bốn trụ cột: Ví dụ thực tế
Chúng ta đã tìm hiểu về bốn trụ cột của OOP. Để hiểu rõ hơn cách chúng hoạt động cùng nhau, hãy xem xét một ví dụ thực tế trong đó cả bốn nguyên lý đều được áp dụng.
Ví dụ: Hệ thống quản lý thư viện
from abc import ABC, abstractmethod
from datetime import datetime, timedelta
# Lớp trừu tượng định nghĩa một tài nguyên thư viện
class LibraryItem(ABC):
def __init__(self, item_id, title, location):
self._item_id = item_id # Protected
self._title = title # Protected
self._location = location # Protected
self.__checked_out = False # Private
self.__due_date = None # Private
@property
def item_id(self):
return self._item_id
@property
def title(self):
return self._title
@property
def location(self):
return self._location
@property
def is_checked_out(self):
return self.__checked_out
@property
def due_date(self):
return self.__due_date
def check_out(self):
if not self.__checked_out:
self.__checked_out = True
self.__due_date = datetime.now() + self.get_checkout_period()
return True
return False
def return_item(self):
if self.__checked_out:
self.__checked_out = False
self.__due_date = None
return True
return False
@abstractmethod
def get_checkout_period(self):
pass
@abstractmethod
def display_info(self):
pass
def get_status(self):
if self.__checked_out:
return f"Checked out, due on {self.__due_date.strftime('%Y-%m-%d')}"
else:
return "Available"
# Các lớp con cho các loại tài nguyên cụ thể
class Book(LibraryItem):
def __init__(self, item_id, title, location, author, isbn, pages):
super().__init__(item_id, title, location)
self.author = author
self.isbn = isbn
self.pages = pages
def get_checkout_period(self):
return timedelta(days=14) # Sách được mượn trong 14 ngày
def display_info(self):
status = self.get_status()
return f"Book: {self._title} by {self.author}, ISBN: {self.isbn}, Pages: {self.pages}, Status: {status}"
class DVD(LibraryItem):
def __init__(self, item_id, title, location, director, runtime, genre):
super().__init__(item_id, title, location)
self.director = director
self.runtime = runtime
self.genre = genre
def get_checkout_period(self):
return timedelta(days=7) # DVD được mượn trong 7 ngày
def display_info(self):
status = self.get_status()
return f"DVD: {self._title}, Director: {self.director}, Runtime: {self.runtime} mins, Genre: {self.genre}, Status: {status}"
class Magazine(LibraryItem):
def __init__(self, item_id, title, location, issue, publisher):
super().__init__(item_id, title, location)
self.issue = issue
self.publisher = publisher
def get_checkout_period(self):
return timedelta(days=2) # Tạp chí chỉ được mượn trong 2 ngày
def display_info(self):
status = self.get_status()
return f"Magazine: {self._title}, Issue: {self.issue}, Publisher: {self.publisher}, Status: {status}"
# Lớp quản lý thư viện
class Library:
def __init__(self, name):
self.name = name
self.items = {}
def add_item(self, item):
if not isinstance(item, LibraryItem):
raise TypeError("Item must be a LibraryItem")
self.items[item.item_id] = item
def find_item(self, item_id):
return self.items.get(item_id)
def check_out_item(self, item_id, patron_id):
item = self.find_item(item_id)
if item and not item.is_checked_out:
if item.check_out():
print(f"Item {item_id} checked out to patron {patron_id}")
return True
print(f"Could not check out item {item_id}")
return False
def return_item(self, item_id):
item = self.find_item(item_id)
if item and item.is_checked_out:
if item.return_item():
print(f"Item {item_id} returned successfully")
return True
print(f"Could not return item {item_id}")
return False
def list_items(self):
for item in self.items.values():
print(item.display_info())
# Sử dụng hệ thống
def main():
# Tạo thư viện
library = Library("Thư viện Thành phố")
# Thêm các tài nguyên
book1 = Book("B001", "Python Programming", "Shelf A1", "John Smith", "978-1234567890", 350)
book2 = Book("B002", "Data Science with Python", "Shelf A2", "Jane Doe", "978-0987654321", 420)
dvd1 = DVD("D001", "The Matrix", "Shelf B1", "Wachowski Brothers", 136, "Sci-Fi")
magazine1 = Magazine("M001", "National Geographic", "Shelf C1", "May 2023", "National Geographic Society")
library.add_item(book1)
library.add_item(book2)
library.add_item(dvd1)
library.add_item(magazine1)
# Liệt kê tất cả các tài nguyên
print("Danh sách tài nguyên trong thư viện:")
library.list_items()
print()
# Mượn một số tài nguyên
library.check_out_item("B001", "P001")
library.check_out_item("D001", "P002")
print()
# Liệt kê lại để xem trạng thái
print("Danh sách sau khi mượn:")
library.list_items()
print()
# Trả lại một tài nguyên
library.return_item("B001")
print()
# Liệt kê lại một lần nữa
print("Danh sách sau khi trả:")
library.list_items()
if __name__ == "__main__":
main()
Tổng kết
Trong bài viết này, chúng ta đã tìm hiểu về bốn trụ cột của lập trình hướng đối tượng:
- Tính đóng gói (Encapsulation): Đóng gói dữ liệu và các phương thức vào một đơn vị, kiểm soát truy cập và bảo vệ dữ liệu.
- Tính kế thừa (Inheritance): Cho phép một lớp kế thừa thuộc tính và phương thức từ lớp khác, tạo ra cấu trúc phân cấp và tái sử dụng mã nguồn.
- Tính đa hình (Polymorphism): Cho phép xử lý các đối tượng khác nhau thông qua một giao diện chung, tạo ra mã nguồn linh hoạt và dễ mở rộng.
- Tính trừu tượng (Abstraction): Tập trung vào các khía cạnh thiết yếu và ẩn đi các chi tiết triển khai, tạo ra giao diện rõ ràng và dễ sử dụng.
Bốn trụ cột này không hoạt động một cách riêng biệt mà thường kết hợp với nhau để tạo ra các thiết kế hướng đối tượng mạnh mẽ và linh hoạt. Python, với cú pháp đơn giản và linh hoạt, là một ngôn ngữ tuyệt vời để học và áp dụng các nguyên lý OOP.
Một số lời khuyên khi áp dụng OOP trong Python
- Sử dụng đúng tính đóng gói: Mặc dù Python không bắt buộc, nhưng hãy tuân thủ các quy ước về đặt tên và sử dụng các thuộc tính, phương thức được bảo vệ và riêng tư một cách thích hợp.
- Tránh lạm dụng đa kế thừa: Đa kế thừa có thể dẫn đến mã nguồn phức tạp và khó hiểu. Nếu có thể, hãy ưu tiên sử dụng composition (kết hợp các đối tượng) thay vì kế thừa.
- Tận dụng các lớp trừu tượng: Sử dụng ABC module để tạo ra các giao diện rõ ràng và đảm bảo các lớp con tuân thủ đúng các phương thức cần thiết.
- Tuân thủ nguyên tắc SOLID: Đây là năm nguyên tắc thiết kế hướng đối tượng giúp tạo ra mã nguồn dễ bảo trì và mở rộng.
Hy vọng bài viết này đã giúp bạn hiểu rõ hơn về bốn trụ cột của OOP và cách áp dụng chúng trong Python. Việc nắm vững các nguyên lý này sẽ giúp bạn trở thành một lập trình viên Python tốt hơn và có khả năng tạo ra các thiết kế phần mềm hiệu quả.
Bài tập thực hành
Để củng cố kiến thức, hãy thử thiết kế và triển khai một hệ thống quản lý sinh viên đơn giản sử dụng cả bốn trụ cột của OOP. Hệ thống này nên có các lớp như Student, Course, Enrollment, và các tính năng như đăng ký khóa học, xem điểm số, v.v.
Tài liệu tham khảo
- Python Documentation: Classes
- Python ABC Module: abc — Abstract Base Classes
- "Clean Code: A Handbook of Agile Software Craftsmanship" by Robert C. Martin
- "Design Patterns: Elements of Reusable Object-Oriented Software" by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides