Hone logo
Hone
Problems

Go Builder Pattern for a Configuration DSL

This challenge focuses on building a Domain Specific Language (DSL) in Go using the Builder pattern. You'll create a flexible and readable way to define and construct complex configuration objects, mimicking how you might set up applications or services. This is a common and powerful technique for managing intricate data structures.

Problem Description

Your task is to design and implement a DSL in Go for defining and constructing a ServerConfig struct. This ServerConfig should represent various aspects of a server, such as its port, hostname, timeout settings, and a list of enabled features. The DSL should allow users to fluently define these configurations without the verbosity of direct struct instantiation, especially when dealing with optional fields or nested configurations.

Key Requirements:

  1. ServerConfig Struct: Define a ServerConfig struct with the following fields:

    • Port (int)
    • Hostname (string)
    • Timeout (time.Duration)
    • Features ([]string)
  2. Builder Pattern: Implement a ServerConfigBuilder that allows for fluent API construction of ServerConfig.

    • The builder should have methods corresponding to each field in ServerConfig (e.g., WithPort(port int), WithHostname(hostname string), WithTimeout(duration time.Duration), WithFeature(feature string)).
    • The WithFeature method should allow adding multiple features to the Features slice.
    • The builder should have a Build() method that returns a ServerConfig instance.
  3. Default Values: The Build() method should handle sensible default values for fields that are not explicitly set by the builder.

    • Default Port: 8080
    • Default Hostname: "localhost"
    • Default Timeout: 30 seconds
    • Default Features: an empty slice
  4. Readability and Fluency: The DSL should allow for construction like this:

    config, err := NewServerConfigBuilder().
        WithPort(9000).
        WithHostname("api.example.com").
        WithTimeout(1 * time.Minute).
        WithFeature("auth").
        WithFeature("logging").
        Build()
    
  5. Error Handling (Optional but Recommended): Consider how to handle potential errors during the build process (e.g., invalid input values). For this challenge, we'll keep it simple and assume valid inputs, but in a real-world scenario, you'd add validation. The Build() method can return an error.

Examples

Example 1: Full Configuration

Input (DSL usage):
builder := NewServerConfigBuilder()
builder.WithPort(9000)
builder.WithHostname("api.example.com")
builder.WithTimeout(1 * time.Minute)
builder.WithFeature("auth")
builder.WithFeature("logging")
config, err := builder.Build()

Output:
ServerConfig{
    Port: 9000,
    Hostname: "api.example.com",
    Timeout: 1m0s,
    Features: ["auth", "logging"],
}
err: nil

Explanation: All fields are explicitly set, overriding defaults.

Example 2: Partial Configuration with Defaults

Input (DSL usage):
builder := NewServerConfigBuilder()
builder.WithPort(8000)
builder.WithFeature("metrics")
config, err := builder.Build()

Output:
ServerConfig{
    Port: 8000,
    Hostname: "localhost",
    Timeout: 30s,
    Features: ["metrics"],
}
err: nil

Explanation: Port and Features are set, while Hostname and Timeout take their default values.

Example 3: No Custom Configuration (All Defaults)

Input (DSL usage):
builder := NewServerConfigBuilder()
config, err := builder.Build()

Output:
ServerConfig{
    Port: 8080,
    Hostname: "localhost",
    Timeout: 30s,
    Features: [],
}
err: nil

Explanation: No custom values are provided, so all fields use their default values.

Constraints

  • The ServerConfig struct must have the specified fields.
  • The builder methods (WithPort, WithHostname, WithTimeout, WithFeature) must return a pointer to the builder itself (*ServerConfigBuilder) to enable method chaining.
  • The Build() method must return a ServerConfig and an error.
  • The default Timeout should be 30 * time.Second.
  • The Features slice should be initialized as an empty slice, not nil.

Notes

  • Consider using time.Duration for the timeout.
  • The builder's internal state should track the values set by the user.
  • When Build() is called, it should create a new ServerConfig and populate it with the user-provided values or the defaults if values were not provided.
  • Think about how to initialize the builder with default values, or how Build() will apply them.
Loading editor...
go