Criar um sistema para gerenciar transações financeiras. Teremos diferentes tipos de Contas (Corrente, Poupança), e o objetivo é que possamos realizar operações comuns, como saque e depósito, de forma polimórfica. Além disso, queremos poder somar (+) o saldo de duas contas de forma intuitiva, independentemente do tipo de conta.

1. Crie uma Interface Abstrata (Conta):

  • Crie uma classe base abstrata Conta (usando abc.ABC).
  • O construtor (init) deve receber saldo_inicial.
  • Defina os métodos abstratos: sacar(valor), depositar(valor) e aplicar_juros().

2. Crie Classes Concretas:

  • Crie a classe ContaCorrente que herda de Conta.
  • O construtor deve inicializar o saldo e um atributo limite (ex: 500.00).
  • Implemente sacar(valor): Deve permitir saque usando o saldo mais o limite. Se o saque usar o limite, imprima um aviso.
  • Implemente depositar(valor): Simplesmente aumenta o saldo.
  • Implemente aplicar_juros(): Não faz nada (juros não se aplicam a Contas Correntes neste cenário).
  • Crie a classe ContaPoupanca que herda de Conta.
  • O construtor deve inicializar o saldo.
  • Implemente sacar(valor): Permite saque somente se houver saldo suficiente.
  • Implemente depositar(valor): Aumenta o saldo.
  • Implemente aplicar_juros(): Adiciona um pequeno juro (ex: 0.5 \% do saldo) ao saldo.

3. Polimorfismo (add):

  • Em ambas as classes (ContaCorrente e ContaPoupanca), sobrescreva o método add(self, outra_conta).
  • Este método deve retornar a soma dos saldos de self e outra_conta.
  1. Teste:
  • Crie instâncias das duas contas.
  • Crie uma lista de contas.
  • Itere sobre a lista para chamar aplicar_juros() em cada conta (demonstrando polimorfismo por subtipo).
  • Use o operador de soma (+) para somar uma ContaCorrente com uma ContaPoupanca (demonstrando polimorfismo de operador).

Passo 1

O enunciado pede que a gente construa um pequeno sistema orientado a objetos para gerenciar transações financeiras com as seguintes exigências principais:
  • Criar uma classe base abstrata chamada Conta com construtor que recebe o saldo inicial e três métodos abstratos: sacar(valor), depositar(valor) e aplicar_juros().
  • Criar duas subclasses concretas:
    • ContaCorrente: tem saldo e um atributo limite (ex.: 500,00); saque pode usar saldo + limite e, caso use o limite, deve exibir um aviso; aplicar_juros() não faz nada.
    • ContaPoupanca: tem saldo; saque só se houver saldo suficiente; aplicar_juros() adiciona juros de 0,5\% ao saldo.
  • Implementar polimorfismo do operador soma sobrescrevendo o método __add__ em ambas as classes para retornar a soma dos saldos das duas contas.
  • Testar: criar instâncias das duas contas, colocar numa lista, iterar chamando aplicar_juros() (polimorfismo por subtipo) e usar o operador + para somar uma ContaCorrente com uma ContaPoupanca (polimorfismo de operador).
Informações relevantes dadas:
  • O limite da conta corrente é 500,00 (valor exemplo).
  • A taxa de juros da poupança é 0,5\%.
  • Precisamos que saque, depósito e juros funcionem conforme descrito e que __add__ some os saldos.

Passo 2

Conceito e esqueleto da classe abstrata:
  • Vamos usar o módulo padrão do Python abc para criar uma classe abstrata. Isso força as subclasses a implementarem os métodos essenciais.
  • A classe Conta terá:
    • um construtor que guarda o saldo inicial em self.saldo;
    • métodos abstratos sacar(self, valor), depositar(self, valor) e aplicar_juros(self) decorados com @abc.abstractmethod.
Por que usar abc.ABC e @abstractmethod?
  • abc.ABC marca a classe como abstrata: não queremos instanciá-la diretamente.
  • @abstractmethod obriga as subclasses a implementar esses métodos; se uma subclasse não implementar, ela também se torna abstrata e não pode ser instanciada.
  • Isso ajuda a garantir que qualquer tipo de conta terá as operações básicas (sacar, depositar, aplicar_juros), permitindo polimorfismo.
Exemplo do esqueleto (apenas a interface, sem lógica):
import abc

class Conta(abc.ABC):
    def __init__(self, saldo_inicial):
        self.saldo = float(saldo_inicial)

    @abc.abstractmethod
    def sacar(self, valor):
        pass

    @abc.abstractmethod
    def depositar(self, valor):
        pass

    @abc.abstractmethod
    def aplicar_juros(self):
        pass

Passo 3

Implementando a classe ContaCorrente:
  • Requisitos:
    • Construtor deve inicializar saldo e limite (usaremos por padrão 500,00).
    • sacar(valor): permite saque enquanto valor <= self.saldo + self.limite. Se valor for maior que self.saldo mas menor ou igual a saldo + limite, isso significa que estamos usando parte do limite — aí mostramos um aviso e deixamos self.saldo possivelmente negativo (representando uso do limite).
    • depositar(valor): acrescenta ao self.saldo.
    • aplicar_juros(): não faz nada.
    • __add__(self, outra_conta): retorna a soma dos saldos (número float).
Detalhes de implementação do saque:
  • Calcular disponivel = self.saldo + self.limite.
  • Se valor <= self.saldo: basta subtrair do saldo.
  • Se self.saldo < valor <= disponivel: subtrair do saldo (pode ficar negativo) e exibir aviso indicando que parte do limite foi usado.
  • Se valor > disponivel: rejeitar o saque (fundos insuficientes).
Código da classe:
class ContaCorrente(Conta):
    def __init__(self, saldo_inicial, limite=500.0):
        super().__init__(saldo_inicial)
        self.limite = float(limite)

    def sacar(self, valor):
        valor = float(valor)
        disponivel = self.saldo + self.limite
        if valor <= self.saldo:
            self.saldo -= valor
            print(f"Saque de {valor:.2f} realizado. Saldo restante: {self.saldo:.2f}")
        elif valor <= disponivel:
            self.saldo -= valor
            print(f"Saque de {valor:.2f} realizado. Aviso: utilizado limite. Saldo atual: {self.saldo:.2f}")
        else:
            print(f"Saque de {valor:.2f} negado. Fundos insuficientes (disponível: {disponivel:.2f}).")

    def depositar(self, valor):
        valor = float(valor)
        self.saldo += valor
        print(f"Depósito de {valor:.2f} realizado. Saldo atual: {self.saldo:.2f}")

    def aplicar_juros(self):
        # Conta corrente não recebe juros neste cenário
        pass

    def __add__(self, outra_conta):
        return self.saldo + outra_conta.saldo

Passo 4

Implementando a classe ContaPoupanca:
  • Requisitos:
    • Construtor inicializa o saldo.
    • sacar(valor): só permite se valor <= self.saldo.
    • depositar(valor): aumenta saldo.
    • aplicar_juros(): aplica juros de 0,5\% — isto é, aumenta o saldo em 0,5\% do valor atual.
    • __add__(self, outra_conta): retorna a soma dos saldos.
Como calcular 0,5\% em código:
  • 0,5\% = 0,005 em decimal.
  • Novo saldo = self.saldo * (1 + 0.005).
Código da classe:
class ContaPoupanca(Conta):
    def __init__(self, saldo_inicial):
        super().__init__(saldo_inicial)

    def sacar(self, valor):
        valor = float(valor)
        if valor <= self.saldo:
            self.saldo -= valor
            print(f"Saque de {valor:.2f} realizado. Saldo restante: {self.saldo:.2f}")
        else:
            print(f"Saque de {valor:.2f} negado. Saldo insuficiente (saldo: {self.saldo:.2f}).")

    def depositar(self, valor):
        valor = float(valor)
        self.saldo += valor
        print(f"Depósito de {valor:.2f} realizado. Saldo atual: {self.saldo:.2f}")

    def aplicar_juros(self):
        taxa = 0.005  # corresponde a 0,5%
        self.saldo *= (1 + taxa)
        print(f"Juros aplicados: {taxa*100:.3f}%. Saldo agora: {self.saldo:.2f}")

    def __add__(self, outra_conta):
        return self.saldo + outra_conta.saldo

Passo 5

Por que sobrescrever __add__ nas duas classes?
  • Polimorfismo de operador: o Python chama a + b como a.__add__(b). Se cada tipo de conta implementa __add__, então a soma funciona independentemente do tipo exato (conta corrente + conta poupança, poupança + poupança etc.).
  • Aqui __add__ retorna um número (a soma dos saldos). Em soluções mais elaboradas poderíamos retornar uma nova Conta, mas o enunciado pede a soma dos saldos, então retornamos um float.

Passo 6

Testando na prática (cenário de exemplo):
  • Vamos criar uma ContaCorrente com saldo inicial de 1000,00 e limite padrão 500,00.
  • Criar uma ContaPoupanca com saldo inicial de 2000,00.
  • Fazer um saque de 1300,00 na conta corrente (deverá usar parte do limite e exibir aviso).
  • Tentar sacar 2100,00 da poupança (deverá ser negado).
  • Colocar as duas contas numa lista e iterar chamando aplicar_juros() em cada (mostrando que para corrente nada acontece e para poupança o juros é aplicado).
  • Somar as duas contas usando + e imprimir o resultado.
Observe que usamos estes valores apenas para demonstrar o comportamento; você pode alterar na hora de testar.

Passo 7

Código de teste completo (pronto para executar). Esse trecho demonstra a criação das contas, operações e o uso do operador +. Execute em um interpretador Python para ver os prints:
import abc

class Conta(abc.ABC):
    def __init__(self, saldo_inicial):
        self.saldo = float(saldo_inicial)

    @abc.abstractmethod
    def sacar(self, valor):
        pass

    @abc.abstractmethod
    def depositar(self, valor):
        pass

    @abc.abstractmethod
    def aplicar_juros(self):
        pass

class ContaCorrente(Conta):
    def __init__(self, saldo_inicial, limite=500.0):
        super().__init__(saldo_inicial)
        self.limite = float(limite)

    def sacar(self, valor):
        valor = float(valor)
        disponivel = self.saldo + self.limite
        if valor <= self.saldo:
            self.saldo -= valor
            print(f"Saque de {valor:.2f} realizado. Saldo restante: {self.saldo:.2f}")
        elif valor <= disponivel:
            self.saldo -= valor
            print(f"Saque de {valor:.2f} realizado. Aviso: utilizado limite. Saldo atual: {self.saldo:.2f}")
        else:
            print(f"Saque de {valor:.2f} negado. Fundos insuficientes (disponível: {disponivel:.2f}).")

    def depositar(self, valor):
        valor = float(valor)
        self.saldo += valor
        print(f"Depósito de {valor:.2f} realizado. Saldo atual: {self.saldo:.2f}")

    def aplicar_juros(self):
        # Conta corrente não recebe juros neste cenário
        print("ContaCorrente: sem aplicação de juros.")

    def __add__(self, outra_conta):
        return self.saldo + outra_conta.saldo

class ContaPoupanca(Conta):
    def __init__(self, saldo_inicial):
        super().__init__(saldo_inicial)

    def sacar(self, valor):
        valor = float(valor)
        if valor <= self.saldo:
            self.saldo -= valor
            print(f"Saque de {valor:.2f} realizado. Saldo restante: {self.saldo:.2f}")
        else:
            print(f"Saque de {valor:.2f} negado. Saldo insuficiente (saldo: {self.saldo:.2f}).")

    def depositar(self, valor):
        valor = float(valor)
        self.saldo += valor
        print(f"Depósito de {valor:.2f} realizado. Saldo atual: {self.saldo:.2f}")

    def aplicar_juros(self):
        taxa = 0.005  # corresponde a 0,5%
        self.saldo *= (1 + taxa)
        print(f"ContaPoupanca: juros de {taxa*100:.3f}% aplicados. Saldo agora: {self.saldo:.2f}")

    def __add__(self, outra_conta):
        return self.saldo + outra_conta.saldo

# Demonstração
if __name__ == "__main__":
    cc = ContaCorrente(1000.0)       # saldo inicial 1000,00; limite padrão 500,00
    cp = ContaPoupanca(2000.0)       # saldo inicial 2000,00

    print(f"Saldo inicial - Conta Corrente: {cc.saldo:.2f}")
    print(f"Saldo inicial - Conta Poupança: {cp.saldo:.2f}\n")

    # Saque que usa limite na conta corrente
    cc.sacar(1300.0)  # deve usar parte do limite e mostrar aviso

    # Saque negado na poupança
    cp.sacar(2100.0)  # saldo insuficiente

    print()
    contas = [cc, cp]
    for conta in contas:
        conta.aplicar_juros()

    print()
    print(f"Saldo final - Conta Corrente: {cc.saldo:.2f}")
    print(f"Saldo final - Conta Poupança: {cp.saldo:.2f}")

    total = cc + cp
    print(f"\nSoma dos saldos (cc + cp): {total:.2f}")

Resposta

import abc

class Conta(abc.ABC):
    def __init__(self, saldo_inicial):
        self.saldo = float(saldo_inicial)

    @abc.abstractmethod
    def sacar(self, valor):
        pass

    @abc.abstractmethod
    def depositar(self, valor):
        pass

    @abc.abstractmethod
    def aplicar_juros(self):
        pass

class ContaCorrente(Conta):
    def __init__(self, saldo_inicial, limite=500.0):
        super().__init__(saldo_inicial)
        self.limite = float(limite)

    def sacar(self, valor):
        valor = float(valor)
        disponivel = self.saldo + self.limite
        if valor <= self.saldo:
            self.saldo -= valor
            print(f"Saque de {valor:.2f} realizado. Saldo restante: {self.saldo:.2f}")
        elif valor <= disponivel:
            self.saldo -= valor
            print(f"Saque de {valor:.2f} realizado. Aviso: utilizado limite. Saldo atual: {self.saldo:.2f}")
        else:
            print(f"Saque de {valor:.2f} negado. Fundos insuficientes (disponível: {disponivel:.2f}).")

    def depositar(self, valor):
        valor = float(valor)
        self.saldo += valor
        print(f"Depósito de {valor:.2f} realizado. Saldo atual: {self.saldo:.2f}")

    def aplicar_juros(self):
        # Conta corrente não recebe juros neste cenário
        print("ContaCorrente: sem aplicação de juros.")

    def __add__(self, outra_conta):
        return self.saldo + outra_conta.saldo

class ContaPoupanca(Conta):
    def __init__(self, saldo_inicial):
        super().__init__(saldo_inicial)

    def sacar(self, valor):
        valor = float(valor)
        if valor <= self.saldo:
            self.saldo -= valor
            print(f"Saque de {valor:.2f} realizado. Saldo restante: {self.saldo:.2f}")
        else:
            print(f"Saque de {valor:.2f} negado. Saldo insuficiente (saldo: {self.saldo:.2f}).")

    def depositar(self, valor):
        valor = float(valor)
        self.saldo += valor
        print(f"Depósito de {valor:.2f} realizado. Saldo atual: {self.saldo:.2f}")

    def aplicar_juros(self):
        taxa = 0.005  # corresponde a 0,5%
        self.saldo *= (1 + taxa)
        print(f"ContaPoupanca: juros de {taxa*100:.3f}% aplicados. Saldo agora: {self.saldo:.2f}")

    def __add__(self, outra_conta):
        return self.saldo + outra_conta.saldo

# Demonstração
if __name__ == "__main__":
    cc = ContaCorrente(1000.0)       # saldo inicial 1000,00; limite padrão 500,00
    cp = ContaPoupanca(2000.0)       # saldo inicial 2000,00

    print(f"Saldo inicial - Conta Corrente: {cc.saldo:.2f}")
    print(f"Saldo inicial - Conta Poupança: {cp.saldo:.2f}\n")

    # Saque que usa limite na conta corrente
    cc.sacar(1300.0)  # deve usar parte do limite e mostrar aviso

    # Saque negado na poupança
    cp.sacar(2100.0)  # saldo insuficiente

    print()
    contas = [cc, cp]
    for conta in contas:
        conta.aplicar_juros()

    print()
    print(f"Saldo final - Conta Corrente: {cc.saldo:.2f}")
    print(f"Saldo final - Conta Poupança: {cp.saldo:.2f}")

    total = cc + cp
    print(f"\nSoma dos saldos (cc + cp): {total:.2f}")

Exercícios relacionados