Menu
Coddy logo textTech

Composição vs Herança

Parte da seção Object Oriented Programming do Journey de Python da Coddy — lição 37 de 64.

A programação orientada a objetos oferece duas abordagens principais para a reutilização de código: herança (relação "é um") e composição (relação "tem um").

Aqui está um exemplo de herança criando uma relação "é-um":

class Animal:
    def __init__(self, name):
        self.name = name
    
    def eat(self):
        return f"{self.name} is eating"

class Dog(Animal):  # Dog "é um" Animal
    def bark(self):
        return "Woof!"

dog = Dog("Buddy")
print(dog.eat())   # Método herdado
print(dog.bark())  # Método próprio

Aqui está um exemplo de composição criando uma relação "tem-um" (has-a):

class Engine:
    def start(self):
        return "Engine started"
    
    def stop(self):
        return "Engine stopped"

class Car:  # Carro "tem um" Motor
    def __init__(self):
        self.engine = Engine()  # Composição
    
    def start(self):
        return self.engine.start()

car = Car()
print(car.start())  # Usa o motor composto

Compare ambas as abordagens com um exemplo mais complexo:

# Abordagem por herança
class Bird:
    def move(self):
        return "Flying"

class Duck(Bird):
    def quack(self):
        return "Quack!"

# Abordagem por composição
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!"

Teste ambas as abordagens:

# Herança
duck1 = Duck()
print(duck1.move())  # Voando
print(duck1.quack()) # Quack!

# Composição  
duck2 = VersatileDuck()
print(duck2.fly())   # Voando
print(duck2.swim())  # Nadando
print(duck2.quack()) # Quack!

Saída:

Buddy is eating
Woof!
Engine started
Flying
Quack!
Flying
Swimming
Quack!

Principais diferenças:

Herança:

  • Acoplamento forte entre pai e filho
  • Relacionamento "É-um"
  • Alterações no pai afetam todos os filhos
  • Melhor para relacionamentos hierárquicos verdadeiros

Composição:

  • Acoplamento fraco entre objetos
  • Relacionamento "Has-a"
  • Mais flexível - pode alterar o comportamento em tempo de execução
  • Mais fácil de testar e modificar

Ponto Chave: Use herança quando você tiver um verdadeiro relacionamento "is-a". Use composição quando precisar de flexibilidade e baixo acoplamento. O princípio "composição sobre herança" sugere favorecer a composição na maioria dos casos devido à sua flexibilidade e manutenibilidade.

challenge icon

Desafio

Médio

Neste desafio, você implementará um sistema de biblioteca de mídia usando os padrões de herança e composição.

Você precisa editar os seguintes arquivos, seguindo os comentários TODO dentro de cada um:

  • media.py, book.py, movie.py, musicalbum.py - Para a implementação de herança
  • mediaitem.py, bookcomposition.py, moviecomposition.py, musicalbumcomposition.py - Para a implementação de composição

Cada tipo de mídia deve ter:

  • Atributos de título (Title), criador (creator - autor/diretor/artista) e ano (year)
  • O método display_info() apropriado

Folha de consulta

A programação orientada a objetos oferece duas abordagens principais para o reaproveitamento de código: herança (relacionamento "é um") e composição (relacionamento "tem um").

Exemplo de herança:

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())   # Inherited method
print(dog.bark())  # Own method

Exemplo de composição:

class Engine:
    def start(self):
        return "Engine started"

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 engine

Principais diferenças:

Herança:

  • Acoplamento forte entre pai e filho
  • Relacionamento "É um"
  • Mudanças no pai afetam todos os filhos
  • Melhor para relacionamentos hierárquicos verdadeiros

Composição:

  • Acoplamento fraco entre objetos
  • Relacionamento "Tem um"
  • Mais flexível - pode mudar o comportamento em tempo de execução
  • Mais fácil de testar e modificar

Use herança para relacionamentos "é um" verdadeiros. Use composição para flexibilidade e acoplamento fraco. O princípio "composição sobre herança" sugere favorecer a composição na maioria dos casos.

Experimente você mesmo

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

# Manipulador abrangente de casos de teste
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():
    # Testa a abordagem de herança
    book = Book("Test Book", "Test Author", 2000)
    assert book.title == "Test Book"
    assert book.creator == "Test Author"
    assert book.year == 2000
    
    # Testa a abordagem de composição
    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():
    # Strings vazias
    book = Book("", "", 0)
    assert book.display_info() == "Book:  by  (0)"
    
    # Caracteres especiais - corrigido o erro de sintaxe aqui
    movie = Movie("Test\"Movie", "Test\\nDirector", -1)
    assert movie.display_info() == "Movie: Test\"Movie directed by Test\\nDirector (-1)"
    
    # Anos extremos
    album = MusicAlbum("Test Album", "Test Artist", 9999)
    assert album.display_info() == "Music Album: Test Album by Test Artist (9999)"

def test_stress():
    # Cria muitos objetos de 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})"

# Executa o teste apropriado com base na entrada
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 iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Object Oriented Programming