Hone logo
Hone
Problems

Advanced Builder Pattern in TypeScript: Customizable Product Creation

The Builder pattern is a creational design pattern that allows you to construct complex objects step-by-step. This challenge focuses on implementing an advanced version of the Builder pattern in TypeScript, enabling flexible and customizable product creation with optional features and chaining capabilities. This is useful when object creation is complex and you want to decouple the construction process from the object's representation.

Problem Description

You are tasked with designing and implementing a Builder pattern for creating Car objects. A Car has several properties: make, model, year, color, engineType (e.g., "V6", "Inline-4"), hasSunroof (boolean), hasNavigation (boolean), and numberOfDoors (integer). The builder should allow clients to specify these properties in a fluent, chainable manner.

Key Requirements:

  • Fluent Interface: The builder methods should return the builder instance itself, allowing for method chaining.
  • Optional Features: hasSunroof and hasNavigation should be optional features that can be enabled or disabled.
  • Default Values: Provide sensible default values for all properties if they are not explicitly set by the client.
  • Validation: Implement basic validation to ensure year is a valid year (e.g., between 1900 and the current year) and numberOfDoors is within a reasonable range (2-5). Throw an error if validation fails.
  • Build Method: A build() method should return a fully constructed Car object.

Expected Behavior:

The client should be able to create Car objects with varying levels of customization. The builder should handle default values and validation gracefully.

Edge Cases to Consider:

  • Invalid year values.
  • Invalid numberOfDoors values.
  • Creating a car with only essential properties (make, model, year).
  • Creating a car with all properties specified.
  • Chaining multiple optional feature settings.

Examples

Example 1:

Input:  new CarBuilder().make("Toyota").model("Camry").year(2023).color("Silver").build()
Output: { make: "Toyota", model: "Camry", year: 2023, color: "Silver", engineType: "Inline-4", hasSunroof: false, hasNavigation: false, numberOfDoors: 4 }
Explanation:  Creates a Camry with default values for engineType, sunroof, navigation, and number of doors.

Example 2:

Input: new CarBuilder().make("Tesla").model("Model S").year(2024).color("Red").engineType("Electric").hasSunroof(true).hasNavigation(true).numberOfDoors(5).build()
Output: { make: "Tesla", model: "Model S", year: 2024, color: "Red", engineType: "Electric", hasSunroof: true, hasNavigation: true, numberOfDoors: 5 }
Explanation: Creates a Tesla Model S with all properties explicitly set.

Example 3: (Edge Case - Invalid Year)

Input: new CarBuilder().make("Ford").model("Mustang").year(1880).build()
Output: Error: Invalid year. Year must be between 1900 and the current year.
Explanation:  Demonstrates validation failing due to an invalid year.

Constraints

  • The year property must be between 1900 and the current year (inclusive).
  • The numberOfDoors property must be between 2 and 5 (inclusive).
  • The Car class should be immutable after construction.
  • The builder should be extensible to support additional car properties in the future without modifying existing code.
  • The code should be well-documented and follow TypeScript best practices.

Notes

  • Consider using TypeScript's optional chaining (?.) to handle optional properties gracefully.
  • Think about how to structure the builder class to promote code reusability and maintainability.
  • The build() method should return a new, immutable Car object. You can achieve immutability by returning a new object with the specified properties rather than modifying an existing one.
  • Focus on creating a clean and expressive API for the builder.
  • Error handling should be clear and informative.
  • The current year can be obtained using new Date().getFullYear().
Loading editor...
typescript