Композиция и наследование
Часть раздела Объектно-ориентированное программирование путешествия по Python на Coddy. Урок 37 из 64.
Объектно-ориентированное программирование предлагает два основных подхода к повторному использованию кода: наследование (отношение «является») и композицию (отношение «имеет»).
Вот пример того, как наследование создаёт отношение «является»:
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()) # Унаследованный метод
print(dog.bark()) # Собственный методВот пример композиции, создающей отношение «имеет»:
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() # Композиция
def start(self):
return self.engine.start()
car = Car()
print(car.start()) # Использует двигатель, включённый в составСравни оба подхода на более сложном примере:
# Подход с использованием наследования
class Bird:
def move(self):
return "Flying"
class Duck(Bird):
def quack(self):
return "Quack!"
# Подход с использованием композиции
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!"Протестируйте оба подхода:
# Наследование
duck1 = Duck()
print(duck1.move()) # Летит
print(duck1.quack()) # Кря!
# Композиция
duck2 = VersatileDuck()
print(duck2.fly()) # Летит
print(duck2.swim()) # Плывёт
print(duck2.quack()) # Кря!Вывод:
Buddy is eating
Woof!
Engine started
Flying
Quack!
Flying
Swimming
Quack!Ключевые различия:
Наследование:
- Тесная связь между родителем и потомком
- Отношение «является»
- Изменения в родителе влияют на всех потомков
- Лучше всего подходит для настоящих иерархических отношений
Композиция:
- Слабая связанность объектов
- Отношение «имеет»
- Более гибкая — можно менять поведение во время выполнения
- Легче тестировать и изменять
Ключевой момент: используй наследование, когда у тебя действительно есть отношение «является». Используй композицию, когда тебе нужны гибкость и слабая связанность. Принцип «композиция вместо наследования» предлагает в большинстве случаев отдавать предпочтение композиции благодаря её гибкости и удобству сопровождения.
Задание
СреднеВ этом задании тебе предстоит реализовать систему медиатеки, используя наследование и композицию.
Тебе нужно отредактировать следующие файлы, следуя комментариям TODO в каждом из них:
media.py,book.py,movie.py,musicalbum.py— для реализации наследованияmediaitem.py,bookcomposition.py,moviecomposition.py,musicalbumcomposition.py— для реализации композиции
У каждого типа медиа должны быть:
- Атрибуты: название, создатель (автор/режиссёр/исполнитель) и год
- Соответствующий метод
display_info()
Попробуйте сами
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
# Обработчик всех тестовых случаев
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():
# Проверка подхода с наследованием
book = Book("Test Book", "Test Author", 2000)
assert book.title == "Test Book"
assert book.creator == "Test Author"
assert book.year == 2000
# Проверка подхода с композицией
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():
# Пустые строки
book = Book("", "", 0)
assert book.display_info() == "Book: by (0)"
# Специальные символы — здесь исправлена синтаксическая ошибка
movie = Movie("Test\"Movie", "Test\\nDirector", -1)
assert movie.display_info() == "Movie: Test\"Movie directed by Test\\nDirector (-1)"
# Крайние значения года
album = MusicAlbum("Test Album", "Test Artist", 9999)
assert album.display_info() == "Music Album: Test Album by Test Artist (9999)"
def test_stress():
# Создать много медиаобъектов
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})"
# Запустить соответствующий тест на основе ввода
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!")В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Введение в ООПВнешние файлыКлассы и объектыПараметр selfМетодыАтрибутыМетод-конструктор (__init__)Повторение — простой калькулятор4Наследование
Основы наследованияФункция super()Переопределение методовМножественное наследованиеПорядок разрешения методовПовторение: иерархия сотрудников7Специальные методы
Введение в магические методыПерегрузка операторовМагические методы контейнеровПовторение — пользовательский список10Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн «Одиночка»Паттерн «Фабрика»Паттерн «Наблюдатель»Паттерн «Стратегия»2Декораторы
Введение в декораторыДекоратор свойстваДекоратор статического методаДекоратор метода класса5Полиморфизм
Переопределение методов: повторениеУтиная типизацияАбстрактные классы и методыПроектирование интерфейсовПовторение — калькулятор фигур8Продвинутые концепции ООП
Композиция и наследованиеМиксиныСтатические методы и методы классаДекораторы классовМенеджеры контекста11Паттерны проектирования. Часть 2
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»3Свойства классов
Переменные экземпляра и классаДекораторы свойствПриватные атрибутыПовторение — менеджер банковского счёта6Инкапсуляция
Публичные, защищённые и приватные членыМодификаторы доступаСокрытие информацииПродвинутые декораторы свойствПовторение — система учёта студентов12Проект: Система управления библиотекой
Обзор проектаКлассы книг и пользователейСистема выдачи книгФункция поискаИнтерфейс администратораТестирование и интеграцияПотренируйтесь самостоятельно: Онлайн-компилятор Python