Embracing Shapes: A Go Polymorphism Challenge
This challenge will guide you through implementing polymorphism in Go using interfaces. You'll create a system that can handle different geometric shapes, allowing you to perform common operations like calculating their area and perimeter in a uniform way, regardless of the specific shape. This is a fundamental concept for building flexible and extensible software.
Problem Description
Your task is to design and implement a Go program that demonstrates polymorphism. You need to create a system that can represent different geometric shapes (e.g., Circle, Rectangle) and perform common operations on them, such as calculating their area and perimeter. The key requirement is to use Go's interface mechanism to achieve polymorphism, meaning you should be able to treat different shape types uniformly through a common interface.
Key Requirements:
- Define a
ShapeInterface: This interface should declare methods forArea()andPerimeter(), both returning afloat64. - Implement Concrete Shape Types: Create at least two concrete types that represent geometric shapes. For this challenge, implement:
Circle: With aRadiusfield.Rectangle: WithWidthandHeightfields.
- Implement Interface Methods: Each concrete shape type must implement the
Shapeinterface by providing implementations forArea()andPerimeter()methods.- Circle Area: π * radius²
- Circle Perimeter: 2 * π * radius
- Rectangle Area: width * height
- Rectangle Perimeter: 2 * (width + height)
- Demonstrate Polymorphism: Create a function that accepts a slice of
Shapeinterface types. This function should iterate through the shapes and print the type of shape, its calculated area, and its perimeter.
Expected Behavior:
The program should correctly calculate and print the area and perimeter for each shape instance, demonstrating that the Area() and Perimeter() methods are called on the appropriate underlying concrete type, even when accessed through the Shape interface.
Edge Cases:
- Consider how your calculations would behave with zero or negative dimensions. For this challenge, assume valid positive dimensions for simplicity.
Examples
Example 1:
Input:
Shapes:
- Circle with radius 5.0
- Rectangle with width 4.0 and height 6.0
Output:
Shape: Circle, Area: 78.53981633974483, Perimeter: 31.41592653589793
Shape: Rectangle, Area: 24.0, Perimeter: 20.0
Explanation: The program iterates through the provided shapes. For the Circle, it calculates Area (π * 5.0²) and Perimeter (2 * π * 5.0). For the Rectangle, it calculates Area (4.0 * 6.0) and Perimeter (2 * (4.0 + 6.0)).
Example 2:
Input:
Shapes:
- Circle with radius 1.0
- Rectangle with width 1.0 and height 1.0
Output:
Shape: Circle, Area: 3.141592653589793, Perimeter: 6.283185307179586
Shape: Rectangle, Area: 1.0, Perimeter: 4.0
Explanation: Similar to Example 1, but with smaller dimensions. This demonstrates correctness for unit values.
Constraints
- All dimensions (radius, width, height) will be positive
float64values. - The number of shapes in the collection will be between 1 and 100.
- The program should not rely on external libraries for mathematical constants like π; use
math.Pifrom the standard library.
Notes
- Think about how an interface in Go defines a "contract." Any type that implements all the methods defined in the interface is considered to satisfy that interface.
- You'll likely need to import the
mathpackage for the value of π. - When printing the shape type, you can use reflection (
fmt.Printf("%T", shape)) or explicitly store the type name as a string within your concrete types. For this challenge, explicitly storing the type name is a good approach.