Early Access: 87 spots left.

Claim
Low Level DesignFactory MethodFactory Method: Centralise Shape Creation

Course content

Factory Method: Centralise Shape Creation

Easy·Tagsdesign-patternscreationalfactory-method

Problem Statement

The starter has a small `Shape` hierarchy with three concrete subclasses (`Circle`, `Square`, `Rectangle`) and a `ShapeFactory` whose `create` method is unimplemented: ```java public static Shape create(String type, double... dimensions) { return null; } ``` Without a working factory, every part of the codebase that needs a shape has to know about every concrete subclass and write its own type-based dispatch: ```java Shape s; if (type.equals("circle")) s = new Circle(radius); else if (type.equals("square")) s = new Square(side); else if (type.equals("rectangle")) s = new Rectangle(width, height); else throw new IllegalArgumentException("Unknown shape: " + type); ``` Duplicate that block across renderer code, importer code, API code, and you have several places that all need to be updated whenever a new shape type is added. Implement `ShapeFactory.create(String type, double... dimensions)` so that there is exactly one place in the codebase that knows how to construct shapes by name. Callers ask the factory for a shape, supply the dimensions, and get back a `Shape` reference. Adding a new shape type later becomes a one-file change. After your refactor, the `create` method must satisfy this contract: - `create("circle", radius)` returns a `Circle` constructed with the given radius. - `create("square", side)` returns a `Square` constructed with the given side length. - `create("rectangle", width, height)` returns a `Rectangle` constructed with the given width and height. - For any other type string, the method throws `IllegalArgumentException` with a message that names the unrecognised type. - The declared return type is `Shape` (the abstraction). Callers depend only on `Shape`, never on a specific concrete subclass. The validator runs five checks: 1. `create("circle", 5)` returns a `Circle` whose `area()` matches the formula for radius 5. 2. `create("square", 4)` returns a `Square` whose `area()` is 16. 3. `create("rectangle", 3, 5)` returns a `Rectangle` whose `area()` is 15. 4. `create("triangle", 1)` throws `IllegalArgumentException`. Unknown types must be rejected explicitly, not return null silently. 5. `ShapeFactory.create`'s declared return type is `Shape`. Reflection check, mirrors the contract that callers depend on the abstraction.

Examples

Example 1
Input
ShapeFactory.create("circle", 5)
Output
"a Shape whose area() is 25π"
Why
The factory builds a Circle with radius 5 and returns it as a Shape reference.
Example 2
Input
ShapeFactory.create("rectangle", 3, 5)
Output
"a Shape whose area() is 15"
Why
Rectangle takes two dimensions: width and height.
Example 3
Input
ShapeFactory.create("hexagon", 6)
Output
"throws IllegalArgumentException"
Why
Unknown type strings are rejected with a message that names the unrecognised type.

Constraints

  • ShapeFactory.create must accept a String type and a double... varargs of dimensions.
  • ShapeFactory.create must return Shape (the abstract supertype), not a concrete subclass.
  • ShapeFactory.create must throw IllegalArgumentException for any type string it does not recognise.
  • The four classes must keep their names: Shape, Circle, Square, Rectangle, ShapeFactory.
  • Type-based branching must live in ShapeFactory only. Callers should not need to know which concrete subclass a Shape ultimately is.

Hints

Stuck? Reveal a nudge toward the right pattern, one step at a time.

Hint 1
A switch statement on the type string is the simplest implementation. case "circle": return new Circle(dimensions[0]); and so on.
Hint 2
Each shape's constructor needs different arguments. Read the docstring on ShapeFactory.create to see which index of dimensions[] each shape consumes.
Hint 3
Add a default branch that throws IllegalArgumentException with a message that includes the offending type, for example `throw new IllegalArgumentException("Unknown shape type: " + type);`. Returning null is not enough.
Hint 4
The declared return type stays Shape. Inside the switch you allocate concrete classes (new Circle, new Square, ...) but the method signature exposes only the abstraction. Callers receive a Shape reference and never need to cast.
Hint 5
If you find yourself writing the same switch in a caller class, that is the smell the factory exists to solve. Move the dispatch into ShapeFactory and have the caller depend on Shape only.