Configuration Objects and the Then Microframework
When parameter lists grow or two kinds of parameters seem to go together a lot, it’s time use the extract parameter object refactoring for greater good – then you can even specify sensible defaults.
This is a typical way to pass a set of options to an objects during initialization in a language like Swift. In languages like JavaScript or Ruby, dictionaries of key–value pairs can work just as well. Using dictionaries in Swift for this can be a pain, though.
Now Soroush wrote about a way that uses the Then microframework as a replacement for configuaration dictionaries. This way you don’t have to promote every property to the initializer’s list of parameters. Here’s a before and after, where you can see that without then
you have to write a lot of repeating boilerplate:
// Before
struct FieldData {
let title: String
let placeholder: String
let keyboardType: UIKeyboardType
let secureEntry: Bool
let autocorrectType: UITextAutocorrectionType
let autocapitalizationType: UITextAutocapitalizationType
init(title: String,
placeholder: String = "",
keyboardType: UIKeyboardType = .Default,
secureEntry: Bool = false,
autocorrectType: UITextAutocorrectionType = .None,
autocapitalizationType: UITextAutocapitalizationType = .None)
{
self.title = title
self.placeholder = placeholder
self.keyboardType = keyboardType
self.secureEntry = secureEntry
self.autocorrectType = autocorrectType
self.autocapitalizationType = autocapitalizationType
}
}
let fieldData = FieldData(title: "Password", secureEntry: true)
Now with then
, making non-mandatory properties mutable and getting rid of the boilerplate:
// After
struct FieldData {
let title: String
var placeholder = ""
var keyboardType = UIKeyboardType.Default
var secureEntry = false
var autocorrectType = UITextAutocorrectionType.No
var autocapitalizationType = UITextAutocapitalizationType.None
init(title: String) {
self.title = title
}
}
let fieldData = FieldData(title: "Password").then({
$0.secureEntry = true
})
That’s a Swift alternative to Ruby’s hash-based option initializers. There, the dictionary’s key is used as the setter’s name which is then invoked like so:
class Example
attr_reader :name, :age
def initialize(args)
args.each do |k,v|
instance_variable_set("@#{k}", v) unless v.nil?
end
end
end
e1 = Example.new :name => 'foo', :age => 33
#=> #<Example:0x3f9a1c @name="foo", @age=33>