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óprioAqui 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 compostoCompare 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.
Desafio
MédioNeste 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çamediaitem.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 methodExemplo 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 enginePrincipais 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!")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
1Fundamentos de POO
Arquivos ExternosIntrodução à POOClasses vs ObjetosO Parâmetro selfMétodosAtributosMétodo Construtor (__init__)Recapitulação - Calculadora Simples4Herança
Herança BásicaA Função super()Sobrescrita de MétodoHerança MúltiplaOrdem de Resolução de MétodosRecapitulação - Hierarquia de Funcionários7Métodos Especiais
Introdução aos Métodos MágicosSobrecarga de OperadoresMétodos Mágicos de ContêinerRecapitulação - Lista Personalizada10Padrões de Projeto Parte 1
Introdução a Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão ObserverPadrão Strategy2Decoradores
Introdução aos DecoradoresDecorador de PropriedadeDecorador de Método EstáticoDecorador de Método de Classe5Polimorfismo
Sobrescrita de Método RevisitadaDuck TypingClasses e Métodos AbstratosDesign de InterfacesRecapitulação - Calculadora de Formas8Conceitos Avançados de POO
Composição vs HerançaMixinsMétodos Estáticos e de ClasseDecoradores de ClasseGerenciadores de Contexto11Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão Composite3Propriedades de Classe
Variáveis de Instância vs de ClasseDecoradores de PropriedadeAtributos PrivadosRecapitulação - Gerenciador de Conta Bancária6Encapsulamento
Membros Públicos, Protegidos e PrivadosModificadores de AcessoOcultação de InformaçãoProperty Decorators AvançadosRecapitulação - Sistema de Registro de Alunos12Projeto: Gerenciamento de Biblioteca
Visão Geral do ProjetoClasses Livro e Usuário