Exercícios da Semana 05

Colocando a Programação Orientada a Objetos em prática.

Objetivos dos Exercícios da Semana 05

Esta semana é dedicada à consolidação dos conceitos de Programação Orientada a Objetos em Python através de exercícios práticos. Ao final, você deverá ser capaz de:

✨ Métodos Mágicos

Implementar e entender o uso de `__str__` e `__eq__` para representação e comparação de objetos.

🧩 Composição

Modelar relações "tem um" entre classes utilizando o conceito de composição.

🚀 Projeto Integrador

Aplicar e combinar todos os pilares da POO (classes, herança, encapsulamento, polimorfismo) em um projeto funcional.

✅ Resolução de Problemas

Desenvolver habilidades de design e resolução de problemas através da prática.

Exercício 1: Implementando `__str__` e `__eq__`

O primeiro exercício foca na personalização do comportamento de seus objetos com métodos mágicos. Você deverá implementar:

  • `__str__`: Para fornecer uma representação de string legível para o usuário (como `print()`).
  • `__eq__`: Para definir como dois objetos da sua classe são comparados por igualdade (`==`).

Exemplo com a Classe `Pessoa`

# Exercício 1: Implemente __str__ e __eq__
class Pessoa:
    def __init__(self, nome, idade, cpf):
        self.nome = nome
        self.idade = idade
        self.cpf = cpf # Assumindo CPF como identificador único

    def __str__(self):
        # Representação de string amigável para o usuário
        return f"Pessoa(Nome: {self.nome}, Idade: {self.idade}, CPF: {self.cpf})"

    def __eq__(self, outro):
        # Define a igualdade com base no CPF
        if not isinstance(outro, Pessoa):
            return NotImplemented # Não sabe comparar com outros tipos
        return self.cpf == outro.cpf

# Exemplo de uso:
# p1 = Pessoa("Maria Silva", 30, "123.456.789-00")
# p2 = Pessoa("Maria Silva", 30, "123.456.789-00") # Mesma pessoa
# p3 = Pessoa("João Souza", 25, "987.654.321-11")

# print(p1) # Chama __str__
# print(f"p1 == p2: {p1 == p2}") # Chama __eq__ (True)
# print(f"p1 == p3: {p1 == p3}") # Chama __eq__ (False)
# print(f"p1 == 'alguma string': {p1 == 'alguma string'}") # Chama __eq__ (False, devido ao NotImplemented)
Image of Dunder Methods

Exercício 2: Composição (Classe Carro e Motor)

Este exercício explora o conceito de composição, uma relação "tem um" entre objetos. Em vez de herdar características, um objeto contém outro como parte de si mesmo.

Exemplo: `Carro` "tem um" `Motor`

# Exercício 2: Crie uma classe Carro que use composição (ela deve ter um objeto Motor como atributo).

class Motor:
    def __init__(self, tipo, potencia):
        self.tipo = tipo
        self.potencia = potencia
        self.ligado = False

    def ligar(self):
        self.ligado = True
        return "Motor ligado!"

    def desligar(self):
        self.ligado = False
        return "Motor desligado."

    def __str__(self):
        return f"Motor(Tipo: {self.tipo}, Potência: {self.potencia} HP, Ligado: {self.ligado})"

class Carro:
    def __init__(self, marca, modelo, tipo_motor, potencia_motor):
        self.marca = marca
        self.modelo = modelo
        # Composição: Um Carro 'tem um' Motor
        self.motor = Motor(tipo_motor, potencia_motor) 
        self.velocidade = 0

    def acelerar(self, incremento):
        if self.motor.ligado:
            self.velocidade += incremento
            return f"Acelerando... Velocidade atual: {self.velocidade} km/h."
        return "Motor desligado. Não é possível acelerar."

    def frear(self, decremento):
        self.velocidade = max(0, self.velocidade - decremento)
        return f"Freando... Velocidade atual: {self.velocidade} km/h."

    def __str__(self):
        status_motor = "ligado" if self.motor.ligado else "desligado"
        return (f"Carro({self.marca} {self.modelo}) - Motor: {self.motor.tipo} ({self.motor.potencia} HP, {status_motor}), "
                f"Velocidade: {self.velocidade} km/h")

# Exemplo de uso:
# meu_motor = Motor("V8", 400)
# meu_carro = Carro("Ford", "Mustang", "V8", 400) # O Carro cria seu próprio Motor internamente

# print(meu_carro)
# print(meu_carro.motor.ligar()) # Acessa o método do objeto Motor através do Carro
# print(meu_carro.acelerar(50))
# print(meu_carro)
# print(meu_carro.motor.desligar())
# print(meu_carro.acelerar(10)) # Não acelera, motor desligado
Image of Composition Example

Exercício 3: Projeto Final - Integrando Conceitos de POO

Este é o desafio final da semana: juntar tudo o que você aprendeu em um pequeno sistema. O objetivo é aplicar classes, herança, encapsulamento e polimorfismo de forma coesa.

Exemplo: Mini Sistema de Biblioteca

Um sistema de biblioteca simples pode demonstrar todos os pilares da POO:

  • Classes: `Livro`, `Membro`, `Biblioteca`.
  • Herança: `LivroDigital` e `LivroFisico` herdando de `Livro`.
  • Encapsulamento: Usar atributos privados/protegidos (ex: `_titulo`, `_membros_ativos`).
  • Polimorfismo: Um método que pode exibir detalhes de qualquer tipo de livro (digital ou físico) ou que pode lidar com diferentes tipos de membros.
# Exercício 3: Projeto Final - Mini Sistema de Biblioteca

# Classe base para Livros
class Livro:
    def __init__(self, titulo, autor, ano_publicacao):
        # Encapsulamento: Usando prefixo "_" para indicar atributos protegidos
        self._titulo = titulo
        self._autor = autor
        self._ano_publicacao = ano_publicacao
        self._disponivel = True

    @property # Getter para _titulo
    def titulo(self):
        return self._titulo

    @property # Getter para _autor
    def autor(self):
        return self._autor

    @property # Getter para _ano_publicacao
    def ano_publicacao(self):
        return self._ano_publicacao

    @property # Getter para _disponivel
    def disponivel(self):
        return self._disponivel

    @disponivel.setter # Setter para _disponivel
    def disponivel(self, status):
        self._disponivel = status

    def descrever(self):
        return f"Título: {self._titulo}, Autor: {self._autor}, Ano: {self._ano_publicacao}"

    def __str__(self):
        return self.descrever()

# Herança: LivroDigital e LivroFisico herdam de Livro
class LivroDigital(Livro):
    def __init__(self, titulo, autor, ano_publicacao, formato):
        super().__init__(titulo, autor, ano_publicacao)
        self.formato = formato # ex: PDF, EPUB

    def descrever(self):
        # Polimorfismo: Sobrescreve o método da classe base
        return f"{super().descrever()}, Formato: {self.formato} (Digital)"

class LivroFisico(Livro):
    def __init__(self, titulo, autor, ano_publicacao, num_paginas):
        super().__init__(titulo, autor, ano_publicacao)
        self.num_paginas = num_paginas

    def descrever(self):
        # Polimorfismo: Sobrescreve o método da classe base
        return f"{super().descrever()}, Páginas: {self.num_paginas} (Físico)"

class Membro:
    def __init__(self, nome, id_membro):
        self.nome = nome
        self.id_membro = id_membro
        self.livros_emprestados = []

    def pegar_emprestado(self, livro):
        if livro.disponivel:
            self.livros_emprestados.append(livro)
            livro.disponivel = False
            return f"{self.nome} emprestou '{livro.titulo}'."
        return f"'{livro.titulo}' não está disponível."

    def devolver(self, livro):
        if livro in self.livros_emprestados:
            self.livros_emprestados.remove(livro)
            livro.disponivel = True
            return f"{self.nome} devolveu '{livro.titulo}'."
        return f"{self.nome} não tinha '{livro.titulo}' emprestado."

    def __str__(self):
        return f"Membro(ID: {self.id_membro}, Nome: {self.nome})"

class Biblioteca:
    def __init__(self, nome):
        self.nome = nome
        self._livros = [] # Encapsulamento
        self._membros = [] # Encapsulamento

    def adicionar_livro(self, livro):
        self._livros.append(livro)
        print(f"Livro '{livro.titulo}' adicionado à biblioteca.")

    def registrar_membro(self, membro):
        self._membros.append(membro)
        print(f"Membro '{membro.nome}' registrado na biblioteca.")

    def listar_livros(self):
        print(f"\nLivros na {self.nome}:")
        if not self._livros:
            print("Nenhum livro cadastrado.")
            return
        for livro in self._livros:
            # Polimorfismo: 'descrever' funciona para Livro, LivroDigital, LivroFisico
            status = "Disponível" if livro.disponivel else "Emprestado"
            print(f"- {livro.descrever()} | Status: {status}")

    def listar_membros(self):
        print(f"\nMembros da {self.nome}:")
        if not self._membros:
            print("Nenhum membro registrado.")
            return
        for membro in self._membros:
            print(f"- {membro}")
            if membro.livros_emprestados:
                emprestados = ", ".join([livro.titulo for livro in membro.livros_emprestados])
                print(f"  Livros emprestados: {emprestados}")
            else:
                print("  Nenhum livro emprestado.")

# Exemplo de uso do Mini Sistema de Biblioteca:
# biblioteca_central = Biblioteca("Biblioteca Central")

# livro_fisico1 = LivroFisico("O Senhor dos Anéis", "J.R.R. Tolkien", 1954, 1200)
# livro_digital1 = LivroDigital("Guia do Mochileiro das Galáxias", "Douglas Adams", 1979, "EPUB")
# livro_fisico2 = LivroFisico("1984", "George Orwell", 1949, 328)

# biblioteca_central.adicionar_livro(livro_fisico1)
# biblioteca_central.adicionar_livro(livro_digital1)
# biblioteca_central.adicionar_livro(livro_fisico2)

# membro1 = Membro("Carlos Alberto", "M001")
# membro2 = Membro("Ana Paula", "M002")

# biblioteca_central.registrar_membro(membro1)
# biblioteca_central.registrar_membro(membro2)

# biblioteca_central.listar_livros()
# biblioteca_central.listar_membros()

# print(membro1.pegar_emprestado(livro_fisico1))
# print(membro2.pegar_emprestado(livro_digital1))
# print(membro1.pegar_emprestado(livro_fisico1)) # Tenta emprestar novamente

# biblioteca_central.listar_livros()
# biblioteca_central.listar_membros()

# print(membro1.devolver(livro_fisico1))
# biblioteca_central.listar_livros()
Image of OOP Concepts

Simulador Interativo: Exercícios 1 e 2

Use este simulador para interagir com as classes simuladas em JavaScript que demonstram o Exercício 1 (`__str__` e `__eq__` com `Pessoa`) e o Exercício 2 (Composição com `Carro` e `Motor`).

Código Python das Classes Simuladas

# Classes Simuladas para o Exercício 1 (Pessoa)
class Pessoa:
    def __init__(self, nome, idade, cpf):
        self.nome = nome
        self.idade = idade
        self.cpf = cpf

    def __str__(self):
        return f"Pessoa(Nome: {self.nome}, Idade: {self.idade}, CPF: {self.cpf})"

    def __eq__(self, outro):
        if not isinstance(outro, Pessoa):
            return NotImplemented
        return self.cpf == outro.cpf

# Classes Simuladas para o Exercício 2 (Carro e Motor)
class Motor:
    def __init__(self, tipo, potencia):
        self.tipo = tipo
        self.potencia = potencia
        self.ligado = False

    def ligar(self):
        self.ligado = True
        return "Motor ligado!"

    def desligar(self):
        self.ligado = False
        return "Motor desligado."

    def __str__(self):
        return f"Motor(Tipo: {self.tipo}, Potência: {self.potencia} HP, Ligado: {self.ligado})"

class Carro:
    def __init__(self, marca, modelo, tipo_motor, potencia_motor):
        self.marca = marca
        self.modelo = modelo
        self.motor = Motor(tipo_motor, potencia_motor) 
        self.velocidade = 0

    def acelerar(self, incremento):
        if self.motor.ligado:
            self.velocidade += incremento
            return f"Acelerando... Velocidade atual: {self.velocidade} km/h."
        return "Motor desligado. Não é possível acelerar."

    def frear(self, decremento):
        self.velocidade = max(0, self.velocidade - decremento)
        return f"Freando... Velocidade atual: {self.velocidade} km/h."

    def __str__(self):
        status_motor = "ligado" if self.motor.ligado else "desligado"
        return (f"Carro({self.marca} {self.modelo}) - Motor: {self.motor.tipo} ({self.motor.potencia} HP, {status_motor}), "
                f"Velocidade: {self.velocidade} km/h")

Simulador - Exercício 1: Classe `Pessoa`

Pessoa 1

Pessoa 2

Simulador - Exercício 2: Composição (Carro e Motor)

Crie seu Carro

Ações do Carro

Resultados do simulador aparecerão aqui.

Próximos Passos: Desafios Finais

Agora que você revisou os exemplos, é hora de aplicar esses conceitos em seus próprios projetos. Os exercícios desta semana são fundamentais para solidificar seu entendimento de POO.

  • 1

    Exercício 1: Implemente o método `__str__` e `__eq__` na sua classe `Pessoa` ou `Livro`.

    Escolha uma das classes que você já criou (ou crie uma nova) e adicione esses métodos mágicos. Teste-os para garantir que funcionam como esperado.

    Dica: Lembre-se que `__str__` é para exibição amigável e `__eq__` é para definir o que significa "igualdade" entre dois objetos.

  • 2

    Exercício 2: Crie uma classe `Carro` que use composição (ela deve ter um objeto `Motor` como atributo).

    Defina uma classe `Motor` e inclua uma instância dela como um atributo dentro da sua classe `Carro`. Pense em como o `Carro` interage com o seu `Motor` (ex: ligar/desligar o motor afeta o carro).

    Dica: Composição é sobre relações "tem um", enquanto herança é sobre relações "é um".

  • 3

    Exercício 3: Projeto Final - Junte todos os conceitos.

    Crie um pequeno sistema (ex: um mini sistema de biblioteca, um jogo de RPG de texto simples) usando classes, herança, encapsulamento e polimorfismo. O objetivo é demonstrar sua capacidade de integrar todos esses conceitos de forma funcional.

    Dica: Comece pequeno! Planeje as classes principais e como elas se relacionarão. Considere as operações que seu sistema precisa realizar (ex: adicionar livro, emprestar, devolver para uma biblioteca; movimentar personagem, atacar para um RPG).