Composition vs Héritage
Fait partie de la section Object Oriented Programming du Journey Python de Coddy — leçon 37 sur 64.
La programmation orientée objet propose deux approches principales pour la réutilisation du code : l'héritage (relation « est un ») et la composition (relation « a un »).
Voici un exemple d'héritage créant une relation « est un » :
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()) # Méthode héritée
print(dog.bark()) # Propre méthodeVoici un exemple de composition créant une relation « a un » (has-a) :
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() # Composition
def start(self):
return self.engine.start()
car = Car()
print(car.start()) # Uses composed engineComparez les deux approches avec un exemple plus complexe :
# Approche par héritage
class Bird:
def move(self):
return "Flying"
class Duck(Bird):
def quack(self):
return "Quack!"
# Approche par composition
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!"Testez les deux approches :
# Héritage
duck1 = Duck()
print(duck1.move()) # Vole
print(duck1.quack()) # Coin-coin !
# Composition
duck2 = VersatileDuck()
print(duck2.fly()) # Vole
print(duck2.swim()) # Nage
print(duck2.quack()) # Coin-coin !Sortie :
Buddy is eating
Woof!
Engine started
Flying
Quack!
Flying
Swimming
Quack!Différences clés :
Héritage :
- Couplage fort entre parent et enfant
- Relation « est un » (Is-a)
- Les modifications apportées au parent affectent tous les enfants
- Idéal pour les véritables relations hiérarchiques
Composition :
- Couplage faible entre les objets
- Relation « Has-a »
- Plus flexible - peut changer de comportement au moment de l'exécution
- Plus facile à tester et à modifier
Point clé : Utilisez l'héritage lorsque vous avez une véritable relation « est un » (is-a). Utilisez la composition lorsque vous avez besoin de flexibilité et d'un couplage faible. Le principe « la composition plutôt que l'héritage » suggère de privilégier la composition dans la plupart des cas en raison de sa flexibilité et de sa maintenabilité.
Défi
MoyenDans ce défi, vous allez implémenter un système de bibliothèque multimédia en utilisant à la fois les modèles d'héritage et de composition.
Vous devez modifier les fichiers suivants, en suivant les commentaires TODO à l'intérieur de chacun :
media.py,book.py,movie.py,musicalbum.py- Pour l'implémentation de l'héritagemediaitem.py,bookcomposition.py,moviecomposition.py,musicalbumcomposition.py- Pour l'implémentation de la composition
Chaque type de média doit avoir :
- Des attributs pour le titre (Title), le créateur (creator) (auteur/réalisateur/artiste) et l'année (year)
- Une méthode
display_info()appropriée
Aide-mémoire
La programmation orientée objet propose deux approches principales pour la réutilisation du code : l'héritage (relation « est un ») et la composition (relation « a un »).
Exemple d'héritage :
class Animal:
def __init__(self, name):
self.name = name
def eat(self):
return f"{self.name} is eating"
class Dog(Animal): # Dog "est un" Animal
def bark(self):
return "Woof!"
dog = Dog("Buddy")
print(dog.eat()) # Méthode héritée
print(dog.bark()) # Propre méthodeExemple de composition :
class Engine:
def start(self):
return "Engine started"
class Car: # Car "a un" Engine
def __init__(self):
self.engine = Engine() # Composition
def start(self):
return self.engine.start()
car = Car()
print(car.start()) # Utilise le moteur composéDifférences clés :
Héritage :
- Couplage fort entre parent et enfant
- Relation « est un »
- Les modifications apportées au parent affectent tous les enfants
- Idéal pour les véritables relations hiérarchiques
Composition :
- Couplage faible entre les objets
- Relation « a un »
- Plus flexible - peut changer de comportement à l'exécution
- Plus facile à tester et à modifier
Utilisez l'héritage pour les véritables relations « est un ». Utilisez la composition pour la flexibilité et le couplage faible. Le principe « la composition plutôt que l'héritage » suggère de privilégier la composition dans la plupart des cas.
Essayez vous-même
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
# Gestionnaire de cas de test complet
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():
# Tester l'approche par héritage
book = Book("Test Book", "Test Author", 2000)
assert book.title == "Test Book"
assert book.creator == "Test Author"
assert book.year == 2000
# Tester l'approche par composition
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():
# Chaînes vides
book = Book("", "", 0)
assert book.display_info() == "Book: by (0)"
# Caractères spéciaux - correction de l'erreur de syntaxe ici
movie = Movie("Test\"Movie", "Test\\nDirector", -1)
assert movie.display_info() == "Movie: Test\"Movie directed by Test\\nDirector (-1)"
# Années extrêmes
album = MusicAlbum("Test Album", "Test Artist", 9999)
assert album.display_info() == "Music Album: Test Album by Test Artist (9999)"
def test_stress():
# Créer de nombreux objets média
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})"
# Exécuter le test approprié en fonction de l'entrée
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!")Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Object Oriented Programming
1Les fondamentaux de la POO
Fichiers externesIntroduction à la POOClasses vs ObjetsLe paramètre selfMéthodesAttributsMéthode constructeur (__init__)Récapitulatif - Calculatrice simple4Héritage
Héritage de baseLa fonction super()Redéfinition de méthodeHéritage multipleOrdre de résolution des méthodesRécapitulatif - Hiérarchie des employés7Méthodes spéciales
Introduction aux méthodes magiquesSurcharge d'opérateursMéthodes magiques de conteneurRécapitulatif - Liste personnalisée10Patrons de conception, Partie 1
Introduction aux patrons de conceptionPatron SingletonPatron FactoryPatron ObserverPatron Strategy2Décorateurs
Introduction aux décorateursDécorateur de propriétéDécorateur de méthode statiqueDécorateur de méthode de classe5Polymorphisme
Retour sur la redéfinition de méthodeDuck TypingClasses et méthodes abstraitesConception d'interfacesRécapitulatif - Calculateur de formes8Concepts avancés de la POO
Composition vs HéritageMixinsMéthodes statiques et de classeDécorateurs de classeGestionnaires de contexte11Patrons de conception, Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Méthode TemplatePatron ÉtatPatron Composite3Propriétés de classe
Variables d'instance vs variables de classeDécorateurs de propriétéAttributs privésRécapitulatif - Gestionnaire de compte bancaire