ℹ️ Select 'Choose Exercise', or randomize 'Next Random Exercise' in selected language.

Choose Exercise:
Timer 00:00
WPM --
Score --
Acc --
Correct chars --

DSL Builder for Configuration Objects

Kotlin

Goal -- WPM

Ready
Exercise Algorithm Area
1import kotlin.properties.Delegates
2
3// Configuration data class
4data class AppConfig(
5val name: String,
6val version: String,
7val logLevel: LogLevel = LogLevel.INFO,
8val timeoutSeconds: Int = 30,
9val featuresEnabled: Set<String> = emptySet(),
10val databaseUrl: String? = null
11)
12
13// Enum for log levels
14enum class LogLevel {
15DEBUG, INFO, WARN, ERROR
16}
17
18// Builder class for AppConfig
19class AppConfigBuilder {
20var name: String by Delegates.notNull()
21var version: String by Delegates.notNull()
22var logLevel: LogLevel = LogLevel.INFO
23var timeoutSeconds: Int = 30
24private val features = mutableSetOf<String>()
25var databaseUrl: String? = null
26
27// DSL method to add a feature
28fun feature(name: String) {
29features.add(name)
30}
31
32// DSL method to configure logging
33fun logging(level: LogLevel = LogLevel.INFO) {
34this.logLevel = level
35}
36
37// DSL method to configure network settings
38fun network(timeout: Int = 30) {
39this.timeoutSeconds = timeout
40}
41
42// Method to build the final configuration object
43fun build(): AppConfig {
44if (!::name.isInitialized || !::version.isInitialized) {
45throw IllegalStateException("App name and version must be set.")
46}
47if (timeoutSeconds <= 0) {
48throw IllegalArgumentException("Timeout must be positive.")
49}
50return AppConfig(
51name = name,
52version = version,
53logLevel = logLevel,
54timeoutSeconds = timeoutSeconds,
55featuresEnabled = features.toSet(), // Ensure immutability
56databaseUrl = databaseUrl
57)
58}
59}
60
61// Extension function to create a DSL context
62fun appConfig(block: AppConfigBuilder.() -> Unit): AppConfig {
63val builder = AppConfigBuilder()
64builder.block() // Apply configuration lambda
65return builder.build()
66}
67
68fun main() {
69try {
70val config = appConfig {
71name = "MyAwesomeApp"
72version = "1.0.0"
73
74logging(LogLevel.DEBUG) // Configure logging
75network(timeout = 60) // Configure network timeout
76
77feature("userAuth") // Enable a feature
78feature("analytics")
79
80databaseUrl = "jdbc:postgresql://localhost:5432/mydb"
81}
82
83println("App Configuration:")
84println(" Name: ${config.name}")
85println(" Version: ${config.version}")
86println(" Log Level: ${config.logLevel}")
87println(" Timeout: ${config.timeoutSeconds}s")
88println(" Features: ${config.featuresEnabled}")
89println(" Database URL: ${config.databaseUrl}")
90
91// Example of invalid configuration (will throw exception)
92// val invalidConfig = appConfig {
93// name = "IncompleteApp"
94// // version is missing
95// timeoutSeconds = -10
96// }
97
98} catch (e: Exception) {
99println("Error creating configuration: ${e.message}")
100}
101}
Algorithm description viewbox

DSL Builder for Configuration Objects

Algorithm description:

This scenario demonstrates how to create a fluent Domain Specific Language (DSL) in Kotlin for building complex configuration objects using the builder pattern. The `appConfig` function acts as the entry point, accepting a lambda receiver of type `AppConfigBuilder`. Inside this lambda, users can fluently set properties and call methods like `logging`, `network`, and `feature` to define their configuration. The `AppConfigBuilder` includes validation to ensure essential properties are set and constraints are met before building the final immutable `AppConfig` object. This pattern is widely used for creating readable and maintainable configurations, such as setting up application parameters, network clients, or database connections.

Algorithm explanation:

The DSL is implemented using a combination of Kotlin's extension functions, lambda receivers, and the builder pattern. The `appConfig` function provides the DSL context. Inside the lambda passed to `appConfig`, `this` refers to an `AppConfigBuilder` instance, allowing direct access to its properties and methods. Properties like `name` and `version` are marked as `by Delegates.notNull()` to enforce their initialization. The `feature`, `logging`, and `network` functions are designed to be called within the DSL block, providing a more readable syntax than direct property assignments for related settings. The `build()` method performs validation checks, throwing exceptions for invalid configurations (e.g., missing required fields, non-positive timeout). This ensures that only well-formed `AppConfig` objects are created. Time complexity for building a configuration is O(F) where F is the number of features enabled, due to adding them to a set. Validation checks are O(1) or O(F) depending on the check. Space complexity is O(F) for storing the features.

Pseudocode:

1. Define the target configuration data class (e.g., `AppConfig`).
2. Define a builder class (e.g., `AppConfigBuilder`) with mutable properties corresponding to the configuration fields.
3. Add methods to the builder for configuring specific aspects (e.g., `logging`, `network`, `feature`).
4. Implement validation logic within the builder's `build` method to check for required fields and constraints.
5. The `build` method should return an immutable instance of the configuration data class.
6. Create an extension function (e.g., `appConfig`) that takes a lambda with the builder as its receiver.
7. Inside the extension function, instantiate the builder, execute the lambda, and call the builder's `build` method.
8. In `main`, use the extension function to create configurations fluently.