Kết hợp và kế thừa
Thuộc phần Lập trình hướng đối tượng trong hành trình Python của Coddy. Bài học 37 trên 64.
Lập trình hướng đối tượng có hai cách chính để tái sử dụng code: kế thừa (quan hệ “là một”) và kết hợp (quan hệ “có một”).
Đây là ví dụ về kế thừa tạo ra mối quan hệ “là một”:
class Animal:
def __init__(self, name):
self.name = name
def eat(self):
return f"{self.name} is eating"
class Dog(Animal): # Dog "is an" Animal
def bark(self):
return "Woof!"
dog = Dog("Buddy")
print(dog.eat()) # Phương thức kế thừa
print(dog.bark()) # Phương thức riêngĐây là ví dụ về cách kết hợp tạo ra mối quan hệ “có một”:
class Engine:
def start(self):
return "Engine started"
def stop(self):
return "Engine stopped"
class Car: # Car "has an" Engine
def __init__(self):
self.engine = Engine() # Quan hệ hợp thành
def start(self):
return self.engine.start()
car = Car()
print(car.start()) # Dùng engine trong quan hệ hợp thànhSo sánh cả hai cách tiếp cận qua một ví dụ phức tạp hơn:
# Cách tiếp cận bằng kế thừa
class Bird:
def move(self):
return "Flying"
class Duck(Bird):
def quack(self):
return "Quack!"
# Cách tiếp cận bằng kết hợp
class FlyBehavior:
def move(self):
return "Flying"
class SwimBehavior:
def move(self):
return "Swimming"
class VersatileDuck:
def __init__(self):
self.fly_behavior = FlyBehavior()
self.swim_behavior = SwimBehavior()
def fly(self):
return self.fly_behavior.move()
def swim(self):
return self.swim_behavior.move()
def quack(self):
return "Quack!"Thử cả hai cách tiếp cận:
# Kế thừa
duck1 = Duck()
print(duck1.move()) # Đang bay
print(duck1.quack()) # Quạc!
# Hợp thành
duck2 = VersatileDuck()
print(duck2.fly()) # Đang bay
print(duck2.swim()) # Đang bơi
print(duck2.quack()) # Quạc!Đầu ra:
Buddy is eating
Woof!
Engine started
Flying
Quack!
Flying
Swimming
Quack!Điểm khác biệt chính:
Kế thừa:
- Sự liên kết chặt chẽ giữa lớp cha và lớp con
- Mối quan hệ “là một”
- Thay đổi ở lớp cha ảnh hưởng đến tất cả lớp con
- Phù hợp nhất với các mối quan hệ phân cấp thực sự
Kết hợp:
- Liên kết lỏng lẻo giữa các đối tượng
- Quan hệ "có một"
- Linh hoạt hơn, có thể thay đổi hành vi khi chương trình đang chạy
- Dễ kiểm thử và chỉnh sửa hơn
Điểm chính: Dùng kế thừa khi có mối quan hệ “là một” thực sự. Dùng kết hợp khi bạn cần tính linh hoạt và sự liên kết lỏng lẻo. Nguyên tắc “ưu tiên kết hợp hơn kế thừa” khuyến khích dùng kết hợp trong hầu hết trường hợp vì tính linh hoạt và dễ bảo trì.
Thử thách
Trung bìnhTrong thử thách này, bạn sẽ triển khai hệ thống thư viện nội dung đa phương tiện bằng cả mẫu kế thừa và mẫu kết hợp.
Bạn cần chỉnh sửa các tệp sau theo chú thích TODO trong từng tệp:
media.py,book.py,movie.py,musicalbum.py, để triển khai kế thừamediaitem.py,bookcomposition.py,moviecomposition.py,musicalbumcomposition.py, để triển khai kết hợp
Mỗi loại nội dung đa phương tiện cần có:
- Các thuộc tính tiêu đề, người sáng tạo (tác giả/đạo diễn/nghệ sĩ) và năm
- Phương thức
display_info()phù hợp
Tự làm thử
from media import Media
from book import Book
from movie import Movie
from musicalbum import MusicAlbum
from mediaitem import MediaItem
from bookcomposition import BookComposition
from moviecomposition import MovieComposition
from musicalbumcomposition import MusicAlbumComposition
# Xử lý toàn diện các test case
test_case = input()
def test_inheritance_basic():
book = Book("The Hobbit", "J.R.R. Tolkien", 1937)
movie = Movie("The Matrix", "Wachowski Sisters", 1999)
album = MusicAlbum("Abbey Road", "The Beatles", 1969)
assert book.display_info() == "Book: The Hobbit by J.R.R. Tolkien (1937)"
assert movie.display_info() == "Movie: The Matrix directed by Wachowski Sisters (1999)"
assert album.display_info() == "Music Album: Abbey Road by The Beatles (1969)"
def test_composition_basic():
book_comp = BookComposition("Dune", "Frank Herbert", 1965)
movie_comp = MovieComposition("Inception", "Christopher Nolan", 2010)
album_comp = MusicAlbumComposition("Thriller", "Michael Jackson", 1982)
assert book_comp.display_info() == "Book: Dune by Frank Herbert (1965)"
assert movie_comp.display_info() == "Movie: Inception directed by Christopher Nolan (2010)"
assert album_comp.display_info() == "Music Album: Thriller by Michael Jackson (1982)"
def test_inheritance_relationships():
book = Book("Test Book", "Test Author", 2000)
movie = Movie("Test Movie", "Test Director", 2001)
album = MusicAlbum("Test Album", "Test Artist", 2002)
assert isinstance(book, Book)
assert isinstance(book, Media)
assert isinstance(movie, Movie)
assert isinstance(movie, Media)
assert isinstance(album, MusicAlbum)
assert isinstance(album, Media)
def test_attribute_access():
# Kiểm tra cách tiếp cận dùng kế thừa
book = Book("Test Book", "Test Author", 2000)
assert book.title == "Test Book"
assert book.creator == "Test Author"
assert book.year == 2000
# Kiểm tra cách tiếp cận dùng thành phần
book_comp = BookComposition("Test Book", "Test Author", 2000)
assert book_comp.media.title == "Test Book"
assert book_comp.media.creator == "Test Author"
assert book_comp.media.year == 2000
def test_polymorphism():
media_list = [
Book("Book1", "Author1", 2001),
Movie("Movie1", "Director1", 2002),
MusicAlbum("Album1", "Artist1", 2003)
]
assert media_list[0].display_info() == "Book: Book1 by Author1 (2001)"
assert media_list[1].display_info() == "Movie: Movie1 directed by Director1 (2002)"
assert media_list[2].display_info() == "Music Album: Album1 by Artist1 (2003)"
def test_edge_cases():
# Chuỗi rỗng
book = Book("", "", 0)
assert book.display_info() == "Book: by (0)"
# Ký tự đặc biệt: đã sửa lỗi cú pháp ở đây
movie = Movie("Test\"Movie", "Test\\nDirector", -1)
assert movie.display_info() == "Movie: Test\"Movie directed by Test\\nDirector (-1)"
# Các giá trị năm ở mức cực hạn
album = MusicAlbum("Test Album", "Test Artist", 9999)
assert album.display_info() == "Music Album: Test Album by Test Artist (9999)"
def test_stress():
# Tạo nhiều đối tượng media
books = [Book(f"Book{i}", f"Author{i}", 2000+i) for i in range(100)]
movies = [Movie(f"Movie{i}", f"Director{i}", 2000+i) for i in range(100)]
albums = [MusicAlbum(f"Album{i}", f"Artist{i}", 2000+i) for i in range(100)]
for i, book in enumerate(books):
assert book.display_info() == f"Book: Book{i} by Author{i} ({2000+i})"
for i, movie in enumerate(movies):
assert movie.display_info() == f"Movie: Movie{i} directed by Director{i} ({2000+i})"
for i, album in enumerate(albums):
assert album.display_info() == f"Music Album: Album{i} by Artist{i} ({2000+i})"
# Chạy bài kiểm tra phù hợp dựa trên đầu vào
if test_case == "default_test" or test_case == "":
test_inheritance_basic()
test_composition_basic()
print("All tests passed!")
elif test_case == "inheritance_test":
test_inheritance_basic()
test_inheritance_relationships()
print("Inheritance tests passed!")
elif test_case == "composition_test":
test_composition_basic()
print("Composition tests passed!")
elif test_case == "polymorphism_test":
test_polymorphism()
print("Polymorphism tests passed!")
elif test_case == "attribute_test":
test_attribute_access()
print("Attribute tests passed!")
elif test_case == "edge_cases":
test_edge_cases()
print("Edge case tests passed!")
elif test_case == "stress_test":
test_stress()
print("Stress tests passed!")Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Nền tảng về lập trình hướng đối tượng
Tệp bên ngoàiGiới thiệu về lập trình hướng đối tượngLớp và đối tượngTham số selfPhương thứcThuộc tínhPhương thức khởi tạo (__init__)Ôn tập: máy tính đơn giản4Kế thừa
Kế thừa cơ bảnHàm super()Ghi đè phương thứcKế thừa đa lớpThứ tự phân giải phương thứcÔn tập: hệ thống phân cấp nhân viên2Decorator
Giới thiệu về decoratorDecorator thuộc tínhDecorator phương thức tĩnhDecorator phương thức lớp5Tính đa hình
Xem lại cách ghi đè phương thứcKiểu gõ vịtLớp và phương thức trừu tượngThiết kế giao diệnÔn tập: máy tính hình học8Các khái niệm OOP nâng cao
Kết hợp và kế thừaMixinPhương thức tĩnh và phương thức lớpDecorator lớpTrình quản lý ngữ cảnhTự luyện tập: Trình biên dịch Python online