Do you need help on a specific subject? Use the contact form (Request a blog entry) on the right hand side.

2016-06-02

Shortening init parameter lists with enum's

When writing an init operation for a moderately complex class (or struct) the number of parameters can explode. Even though a lot of them will probably have decent default values.

Theory tells us that 7 should be the max, but actually, even 7 is already too much.

So, what other alternatives are there?

There are two possible approaches that are often used:

1) Before object initialization

By creating an initialization options object. This object is initialized before calling the init operation and passed as a single parameter. While this can be useful if many objects have to be instantiated it usually does not much to improve code readability.

2)  After object initialization

Provide default values and make the properties accessable from the outside. Then after the initialization of the object the properties are set to the correct values. This too does nothing to improve the readability of the code, and it can cause problems if the properties really should not be modifiable after initialization.

Swift gives us a third way: Enum's with associated values, variadic parameters and optionals.

Nothing shows this better than a small example:

 class FileLog {
    
    enum InitOption {
        case MaxFileSize(Int)
        case MaxNofFiles(Int)
        case NewFileAfterDelay(NSTimeInterval)
        case NewFileDailyAt(NSDate)
    }
    
    private var filename: String
    private var fileExtension: String
    
    private var maxFileSize: Int?
    private var maxNofFiles: Int?
    private var newFileDailyAt: NSDate?
    private var newFileAfterDelay: NSTimeInterval?
    
    init?(filename: String = "logfile", fileExtension: String = "txt", options: InitOption ...) {
        
        self.filename = filename
        self.fileExtension = fileExtension
        
        for option in options {
            switch option {
            case let .MaxFileSize(size): self.maxFileSize = size
            case let .MaxNofFiles(num): self.maxNofFiles = num
            case let .NewFileAfterDelay(delay): self.newFileAfterDelay = delay
            case let .NewFileDailyAt(time): self.newFileDailyAt = time
            }
        }
    }
}

The variadic parameter 'options' allows us to only specify those properties we want to initialize. The enum with associated values gives us a way to name the properties and their values and the optionals let us know afterwards which options the callee did want to use and which not.

An example of creating an instance:

let lf = FileLog(
    filename: "MyLog",
    fileExtension: "txt",
    options:
        .MaxFileSize(20),
        .NewFileAfterDelay(3600.0)
    )

This avoids unnecessary code and does all the initialization exactly in the place it needs to be. None of the properties that are set are visible to the outside and internally we can test easily if an option is set or not.

It goes without saying that the necessary documentation should be provided in the enum and its cases. ;-)

PS: Using key/value coding it would even be possible to replace the loop with generic code to set the optional properties, however that would be taking a good thing too far imo.

Happy coding...

Did this help?, then please help out a small independent.
If you decide that you want to make a small donation, you can do so by clicking this
link: a cup of coffee ($2) or use the popup on the right hand side for different amounts.
Payments will be processed by PayPal, receiver will be sales at balancingrock dot nl
Bitcoins will be gladly accepted at: 1GacSREBxPy1yskLMc9de2nofNv2SNdwqH

We don't get the world we wish for... we get the world we pay for.

No comments:

Post a Comment