Menu
Coddy logo textTech

Recapitulação - Calculadora de Formas

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

challenge icon

Desafio

Médio

Neste desafio, você implementará um sistema de calculadora de formas que demonstra polimorfismo.

Implemente as classes necessárias nos seguintes arquivos (procure pelos comentários TODO em cada arquivo):

  • shape.py - Defina a classe base abstrata Shape
  • circle.py - Implemente a classe Circle
  • rectangle.py - Implemente a classe Rectangle
  • triangle.py - Implemente a classe Triangle
  • unknownshape.py - Crie uma classe não derivada demonstrando duck typing
  • shapecalculator.py - Implemente a funcionalidade da calculadora

Siga os comentários TODO em cada arquivo para orientação passo a passo. A suíte de testes abrangente ajudará você a entender o comportamento esperado e garantirá que sua implementação atenda a todos os requisitos corretamente.

Folha de consulta

Como não há conteúdo de lição fornecido, apenas instruções de desafio, não posso criar uma folha de dicas. As folhas de dicas devem ser baseadas principalmente na teoria e nos conceitos da lição, com o conteúdo do desafio apenas complementando quando contiver explicações úteis adicionais não cobertas na lição.

Experimente você mesmo

from circle import Circle
from rectangle import Rectangle
from triangle import Triangle
from unknownshape import UnknownShape
from shapecalculator import ShapeCalculator
import math

# Manipulador de casos de teste
test_case = input()

def test_basic_functionality():
    calculator = ShapeCalculator()
    
    # Teste com Círculo
    circle = Circle(5)
    print(f"Testing {circle}")
    results = calculator.process_shape(circle)
    print(f"Results: {results}")
    print()
    
    # Teste com Retângulo
    rectangle = Rectangle(4, 6)
    print(f"Testing {rectangle}")
    results = calculator.process_shape(rectangle)
    print(f"Results: {results}")
    print()
    
    # Teste com Triângulo
    triangle = Triangle(3, 4, 5)
    print(f"Testing {triangle}")
    results = calculator.process_shape(triangle)
    print(f"Results: {results}")
    print()
    
    # Teste com UnknownShape (duck typing)
    unknown = UnknownShape("Custom", 10)
    print(f"Testing {unknown}")
    results = calculator.process_shape(unknown)
    print(f"Results: {results}")

def test_edge_cases():
    calculator = ShapeCalculator()
    
    # Teste com valores zero
    print("Testing zero values:")
    circle = Circle(0)
    print(f"Circle(0) area: {circle.area()}, perimeter: {circle.perimeter()}")
    
    rectangle = Rectangle(0, 5)
    print(f"Rectangle(0, 5) area: {rectangle.area()}, perimeter: {rectangle.perimeter()}")
    
    # Teste com valores muito grandes
    print("\
Testing large values:")
    large_circle = Circle(1000000)
    print(f"Circle(1000000) area: {large_circle.area():.2e}, perimeter: {large_circle.perimeter():.2e}")
    
    # Teste com valores decimais
    print("\
Testing decimal values:")
    decimal_circle = Circle(0.5)
    print(f"Circle(0.5) area: {decimal_circle.area()}, perimeter: {decimal_circle.perimeter()}")
    
    decimal_triangle = Triangle(2.5, 3.5, 4.5)
    print(f"Triangle(2.5, 3.5, 4.5) area: {decimal_triangle.area()}, perimeter: {decimal_triangle.perimeter()}")

def test_inheritance():
    # Cria instâncias de cada forma
    circle = Circle(5)
    rectangle = Rectangle(4, 6)
    triangle = Triangle(3, 4, 5)
    unknown = UnknownShape("Custom", 10)
    
    # Testa relações de herança
    from shape import Shape
    print(f"Circle is a Shape: {isinstance(circle, Shape)}")
    print(f"Rectangle is a Shape: {isinstance(rectangle, Shape)}")
    print(f"Triangle is a Shape: {isinstance(triangle, Shape)}")
    print(f"UnknownShape is a Shape: {isinstance(unknown, Shape)}")
    
    # Testa disponibilidade de métodos
    print("\
Method availability:")
    for shape_name, shape_obj in [("Circle", circle), ("Rectangle", rectangle), 
                                ("Triangle", triangle), ("UnknownShape", unknown)]:
        print(f"{shape_name} has area(): {hasattr(shape_obj, 'area')}")
        print(f"{shape_name} has perimeter(): {hasattr(shape_obj, 'perimeter')}")
        print(f"{shape_name} has describe(): {hasattr(shape_obj, 'describe')}")

def test_polymorphism():
    calculator = ShapeCalculator()
    
    # Cria uma lista de diferentes formas
    shapes = [
        Circle(5),
        Rectangle(4, 6),
        Triangle(3, 4, 5),
        UnknownShape("Custom", 10)
    ]
    
    print("Testing polymorphic behavior:")
    for i, shape in enumerate(shapes, 1):
        print(f"\
Shape {i}: {shape.__class__.__name__}")
        results = calculator.process_shape(shape)
        print(f"Results: {results}")

def test_duck_typing():
    calculator = ShapeCalculator()
    
    # Cria uma classe completamente nova que possui os métodos necessários
    class CustomDuckShape:
        def __init__(self, name, factor):
            self.name = name
            self.factor = factor
        
        def area(self):
            return self.factor * 3
        
        def perimeter(self):
            return self.factor * 12
        
        def describe(self):
            return f"This is a {self.name} duck-typed shape with area {self.area()} and perimeter {self.perimeter()}"
        
        def __str__(self):
            return f"Custom {self.name} with factor {self.factor}"
    
    # Teste com a forma customizada via duck-typing
    duck_shape = CustomDuckShape("DuckTyped", 5)
    print(f"Testing duck typing with: {duck_shape}")
    results = calculator.process_shape(duck_shape)
    print(f"Results: {results}")
    
    # Compara com uma forma regular
    circle = Circle(5)
    print(f"\
Comparing with regular shape: {circle}")
    results = calculator.process_shape(circle)
    print(f"Results: {results}")

def test_invalid_inputs():
    calculator = ShapeCalculator()
    
    try:
        # Teste com raio negativo
        print("Testing Circle with negative radius:")
        negative_circle = Circle(-5)
        results = calculator.process_shape(negative_circle)
        print(f"Results: {results}")
    except Exception as e:
        print(f"Error with negative circle: {e}")
    
    try:
        # Teste com triângulo inválido (lados que não podem formar um triângulo)
        print("\
Testing invalid Triangle (1, 1, 10):")
        invalid_triangle = Triangle(1, 1, 10)  # Viola a desigualdade triangular
        results = calculator.process_shape(invalid_triangle)
        print(f"Results: {results}")
        # Verifica se o cálculo da área retorna um resultado válido
        area = invalid_triangle.area()
        if math.isnan(area) or not isinstance(area, float) or area <= 0:
            print(f"Invalid triangle area: {area}")
    except Exception as e:
        print(f"Error with invalid triangle: {e}")

# Executa o teste apropriado com base na entrada
if test_case == "basic_functionality":
    test_basic_functionality()
elif test_case == "edge_cases":
    test_edge_cases()
elif test_case == "inheritance":
    test_inheritance()
elif test_case == "polymorphism":
    test_polymorphism()
elif test_case == "duck_typing":
    test_duck_typing()
elif test_case == "invalid_inputs":
    test_invalid_inputs()
else:
    # Caso de teste padrão
    test_basic_functionality()

Todas as lições de Object Oriented Programming