The Configuration DSL pattern helps you write easy-to-read code that sets up settings or options in a clear way, like filling out a form.
Configuration DSL pattern in Kotlin
class Config { var option1: String = "" var option2: Int = 0 fun build(block: Config.() -> Unit): Config { block() return this } } // Usage: val config = Config().build { option1 = "Hello" option2 = 42 }
The block is a function that runs with Config as its receiver, so you can set properties directly.
This pattern makes configuration code look clean and easy to read.
class ServerConfig { var host: String = "localhost" var port: Int = 8080 fun configure(block: ServerConfig.() -> Unit): ServerConfig { block() return this } } val server = ServerConfig().configure { host = "192.168.1.1" port = 9090 }
class DatabaseConfig { var url: String = "" var user: String = "" var password: String = "" fun setup(block: DatabaseConfig.() -> Unit): DatabaseConfig { block() return this } } val dbConfig = DatabaseConfig().setup { url = "jdbc:mysql://localhost:3306/mydb" user = "root" password = "secret" }
This program creates an email configuration using the DSL pattern. It sets the SMTP server, port, and SSL option inside a block, then prints the settings.
class EmailConfig { var smtpServer: String = "" var port: Int = 25 var useSSL: Boolean = false fun configure(block: EmailConfig.() -> Unit): EmailConfig { block() return this } } fun main() { val emailConfig = EmailConfig().configure { smtpServer = "smtp.example.com" port = 587 useSSL = true } println("SMTP Server: ${emailConfig.smtpServer}") println("Port: ${emailConfig.port}") println("Use SSL: ${emailConfig.useSSL}") }
You can add default values to make configuration easier.
The block uses this as the receiver, so you can access properties directly without extra names.
This pattern improves code readability and reduces errors in setting options.
The Configuration DSL pattern helps write clear and simple setup code.
It uses a block with the configuration object as receiver to set options easily.
This makes your code look like a small language for configuring things.
Practice
Solution
Step 1: Understand the Configuration DSL pattern
This pattern is designed to make configuration code simple and clear by using blocks with a configuration object as receiver.Step 2: Identify the main purpose
The main goal is to write setup code that looks like a small language, making it easy to read and write configurations.Final Answer:
To create a simple and readable way to set up configuration using blocks -> Option CQuick Check:
Configuration DSL = simple setup blocks [OK]
- Confusing DSL with general programming logic
- Thinking it manages databases or exceptions
- Assuming it is for algorithm implementation
Solution
Step 1: Identify the correct DSL block signature
A configuration DSL block uses a receiver function type like Config.() -> Unit to allow calling config properties inside the block.Step 2: Check the function implementation
fun configure(block: Config.() -> Unit) { val config = Config(); config.block() } creates a Config instance and calls the block with config as receiver, which is the correct pattern.Final Answer:
fun configure(block: Config.() -> Unit) { val config = Config(); config.block() } -> Option DQuick Check:
Receiver function type = fun configure(block: Config.() -> Unit) { val config = Config(); config.block() } [OK]
- Using () -> Config instead of Config.() -> Unit
- Passing Config instance as parameter instead of receiver
- Omitting the receiver call inside the function
class Config { var name = ""; var enabled = false }
fun configure(block: Config.() -> Unit): Config {
val config = Config()
config.block()
return config
}
fun main() {
val c = configure {
name = "Test"
enabled = true
}
println("Name: ${c.name}, Enabled: ${c.enabled}")
}Solution
Step 1: Understand the configure function
The configure function creates a Config object, applies the block to set properties, then returns it.Step 2: Analyze the block in main
The block sets name to "Test" and enabled to true on the Config instance.Step 3: Check the printed output
Printing c.name and c.enabled shows "Test" and true respectively.Final Answer:
Name: Test, Enabled: true -> Option AQuick Check:
Block sets properties correctly = Name: Test, Enabled: true [OK]
- Assuming default values remain unchanged
- Thinking block does not modify config
- Confusing syntax causing compilation error
class Config { var port = 0 }
fun configure(block: Config.() -> Unit): Config {
val config = Config()
block()
return config
}
fun main() {
val c = configure {
port = 8080
}
println(c.port)
}Solution
Step 1: Check how block is called inside configure
The block is called as block() without receiver, so it does not modify the config instance.Step 2: Understand the effect on port property
Since the block does not apply to config, port remains 0, the default value.Final Answer:
The block is called without receiver, so port is not set -> Option AQuick Check:
Missing receiver call = The block is called without receiver, so port is not set [OK]
- Calling block() instead of config.block()
- Assuming default constructor is missing
- Thinking return type causes error
Choose the best approach.
Solution
Step 1: Understand nested configuration needs
To configure server and database settings clearly, nested blocks with receivers allow intuitive setup.Step 2: Use nested classes and receiver blocks
Define ServerConfig with a DatabaseConfig property and provide a nested block to configure database inside server block.Step 3: Why other options are less suitable
Global variables reduce clarity, passing strings loses structure, separate functions lose nesting benefits.Final Answer:
Define nested classes for ServerConfig and DatabaseConfig, then use nested receiver blocks -> Option BQuick Check:
Nested classes + blocks = Define nested classes for ServerConfig and DatabaseConfig, then use nested receiver blocks [OK]
- Using global variables instead of nested blocks
- Passing unstructured strings for nested config
- Avoiding nested blocks reduces readability
Ready to prove it?
Pass the test and this pattern moves unknown
