Kompozycja a dziedziczenie
Część sekcji Programowanie obiektowe ścieżki Python w Coddy. Lekcja 37 z 64.
Programowanie obiektowe oferuje dwa główne podejścia do ponownego wykorzystania kodu: dziedziczenie (relacja „jest”) i kompozycję (relacja „ma”).
Oto przykład dziedziczenia tworzącego relację „jest czymś”:
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()) # Metoda odziedziczona
print(dog.bark()) # Własna metodaOto przykład kompozycji tworzącej relację „ma”:
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() # Kompozycja
def start(self):
return self.engine.start()
car = Car()
print(car.start()) # Używa silnika będącego częścią kompozycjiPorównaj oba podejścia na bardziej złożonym przykładzie:
# Podejście oparte na dziedziczeniu
class Bird:
def move(self):
return "Flying"
class Duck(Bird):
def quack(self):
return "Quack!"
# Podejście oparte na kompozycji
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!"Przetestuj oba podejścia:
# Dziedziczenie
duck1 = Duck()
print(duck1.move()) # Latanie
print(duck1.quack()) # Kwa!
# Kompozycja
duck2 = VersatileDuck()
print(duck2.fly()) # Latanie
print(duck2.swim()) # Pływanie
print(duck2.quack()) # Kwa!Wynik:
Buddy is eating
Woof!
Engine started
Flying
Quack!
Flying
Swimming
Quack!Najważniejsze różnice:
Dziedziczenie:
- Ścisłe powiązanie między klasą nadrzędną a podrzędną
- Relacja „jest rodzajem”
- Zmiany w klasie nadrzędnej wpływają na wszystkie klasy podrzędne
- Najlepsze w przypadku rzeczywistych relacji hierarchicznych
Kompozycja:
- Luźne powiązanie między obiektami
- Relacja „ma”
- Większa elastyczność — zachowanie można zmieniać w czasie wykonywania
- Łatwiejsze testowanie i modyfikowanie
Najważniejsza kwestia: Używaj dziedziczenia, gdy zachodzi prawdziwa relacja „jest-rodzajem”. Używaj kompozycji, gdy potrzebujesz elastyczności i luźnego powiązania. Zasada „kompozycja zamiast dziedziczenia” sugeruje, by w większości przypadków wybierać kompozycję ze względu na jej elastyczność i łatwość utrzymania.
Wyzwanie
ŚredniW tym wyzwaniu zaimplementujesz system biblioteki multimediów, wykorzystując zarówno dziedziczenie, jak i kompozycję.
Musisz edytować następujące pliki, postępując zgodnie z komentarzami TODO w każdym z nich:
media.py,book.py,movie.py,musicalbum.py— na potrzeby implementacji dziedziczeniamediaitem.py,bookcomposition.py,moviecomposition.py,musicalbumcomposition.py— na potrzeby implementacji kompozycji
Każdy typ medium powinien mieć:
- Atrybuty: tytuł, twórca (autor/reżyser/artysta) i rok
- Odpowiednią metodę
display_info()
Spróbuj swoich sił
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
# Kompleksowa obsługa przypadków testowych
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():
# Testowanie podejścia opartego na dziedziczeniu
book = Book("Test Book", "Test Author", 2000)
assert book.title == "Test Book"
assert book.creator == "Test Author"
assert book.year == 2000
# Testowanie podejścia opartego na kompozycji
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():
# Puste ciągi znaków
book = Book("", "", 0)
assert book.display_info() == "Book: by (0)"
# Znaki specjalne — tutaj poprawiono błąd składniowy
movie = Movie("Test\"Movie", "Test\\nDirector", -1)
assert movie.display_info() == "Movie: Test\"Movie directed by Test\\nDirector (-1)"
# Skrajne wartości lat
album = MusicAlbum("Test Album", "Test Artist", 9999)
assert album.display_info() == "Music Album: Test Album by Test Artist (9999)"
def test_stress():
# Utwórz wiele obiektów mediów
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})"
# Uruchom odpowiedni test na podstawie danych wejściowych
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!")Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrzneWprowadzenie do programowania obiektowegoKlasy a obiektyParametr selfMetodyAtrybutyMetoda konstruktora (__init__)Powtórzenie – prosty kalkulator4Dziedziczenie
Podstawy dziedziczeniaFunkcja super()Nadpisywanie metodDziedziczenie wielokrotneKolejność rozwiązywania metodPodsumowanie – hierarchia pracowników7Metody specjalne
Wprowadzenie do metod magicznychPrzeciążanie operatorówMagiczne metody kontenerówPowtórka – własna lista10Wzorce projektowe, część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonWzorzec FactoryWzorzec ObserverWzorzec Strategy2Dekoratory
Wprowadzenie do dekoratorówDekorator propertyDekorator metody statycznejDekorator metody klasowej5Polimorfizm
Ponowne omówienie nadpisywania metodTypowanie kaczeKlasy i metody abstrakcyjneProjektowanie interfejsówPodsumowanie — kalkulator kształtów8Zaawansowane koncepcje programowania obiektowego
Kompozycja a dziedziczenieMiksyMetody statyczne i klasoweDekoratory klasMenedżery kontekstu11Wzorce projektowe, część 2
Wzorzec PolecenieWzorzec AdapterWzorzec DekoratorWzorzec Metoda szablonowaWzorzec StanWzorzec Kompozyt3Właściwości klas
Zmienne instancji a zmienne klasoweDekoratory właściwościAtrybuty prywatnePowtórzenie — menedżer kont bankowych6Hermetyzacja
Public, Protected, Private — pamięćModyfikatory dostępuUkrywanie informacjiDekoratory właściwości — zaawansowanePodsumowanie — system ewidencji uczniów12Projekt: System zarządzania biblioteką
Przegląd projektuKlasy Book i UserSystem wypożyczeńFunkcja wyszukiwaniaInterfejs administratoraTestowanie i integracjaPoćwicz samodzielnie: Kompilator Python online