Challenge 2 — Solution Task: Create a Shape interface with a getArea() method, two implementations (Circle, Square), and a ShapeFactory with a static create(string $type, ...$args) method using match() to construct the right one. Create one of each via the factory and echo their areas. radius ** 2; } } class Square implements Shape { public function __construct(private float $side) {} public function getArea(): float { return $this->side ** 2; } } class ShapeFactory { public static function create(string $type, ...$args): Shape { return match ($type) { 'circle' => new Circle(...$args), 'square' => new Square(...$args), default => throw new InvalidArgumentException("Unknown shape type: $type"), }; } } $circle = ShapeFactory::create('circle', 5); $square = ShapeFactory::create('square', 4); echo "Circle area: " . round($circle->getArea(), 2) . "
"; echo "Square area: " . $square->getArea(); ?> Output: Circle area: 78.54 Square area: 16 Notes: - ...$args (the "splat" operator) collects any number of extra arguments passed to create() into an array, then new Circle(...$args) "unpacks" them back out as individual constructor arguments - letting one factory method handle constructors with genuinely different parameter counts (Circle takes 1 arg, and a more complex shape could take more) without ShapeFactory needing to know each shape's specific signature in advance. - Calling code never references Circle or Square directly - it only ever interacts with ShapeFactory::create() and the returned Shape interface, exactly the decoupling benefit the chapter's own NotificationFactory example demonstrated. - match() throws a clear InvalidArgumentException for any unrecognised $type, exactly mirroring the chapter's own NotificationFactory pattern for handling an unknown value.