Menu
Coddy logo textTech

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 metoda

Oto 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ą kompozycji

Poró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.

challenge icon

Wyzwanie

Średni

W 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 dziedziczenia
  • mediaitem.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!")
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator Python online