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:
-
ServerConfigStruct: Define aServerConfigstruct with the following fields:Port(int)Hostname(string)Timeout(time.Duration)Features([]string)
-
Builder Pattern: Implement a
ServerConfigBuilderthat allows for fluent API construction ofServerConfig.- 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
WithFeaturemethod should allow adding multiple features to theFeaturesslice. - The builder should have a
Build()method that returns aServerConfiginstance.
- The builder should have methods corresponding to each field in
-
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
- Default
-
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() -
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
ServerConfigstruct 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 aServerConfigand anerror. - The default
Timeoutshould be30 * time.Second. - The
Featuresslice should be initialized as an empty slice, notnil.
Notes
- Consider using
time.Durationfor the timeout. - The builder's internal state should track the values set by the user.
- When
Build()is called, it should create a newServerConfigand 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.