Tuesday, February 2, 2016

Refactoring: Replace magic numbers with constants

Magic numbers are values with unexplained meaning and multiple occurrences in the code, according to Wikipedia. Here is an example:

func validateUsername(username:String?) {
    if let username = username {
        let length = username.characters.count
        if length <= 3 {
            print("Username too short.")
            print("It needs to be longer than 3.")
        } else if length >= 10 {
            print("Username too long.")
            print("It needs to be shorter than 10.")
        } else {
            print("username valid")
        }
    } else {
        print("username cannot be nil")
    }
}


We recognize right away the "magic numbers" as the minimum and the maximum length for a password, 3 respectively 10. We see they are present inside the error message so there is the danger that, when one of the values change, we'll forget to change the other one:

...
        if length <= 4 {
            print("Username too short.")
            print("It needs to be longer than 3.")
        }
...

So magic numbers are bad. The solution is replacing them with constants. For example:

let kMinimumLength = 3
let kMaximumLength = 10

func validateUsername(username:String?) {
    if let username = username {
        let length = username.characters.count
        if length <= kMinimumLength {
            print("Username too short.")
            print("It needs to be longer than \(kMinimumLength)")
        } else if length >= kMaximumLength {
            print("Username too long.")
            print("It needs to be shorter than \(kMaximumLength)")
        } else {
            print("username valid")
        }
    } else {
        print("username cannot be nil")
    }
}

If an update is needed, we need to change only the value of the constants.

The constants can be used across the class and maybe in other classes if defined as global.

We can even go further with creating the constants. 

We notice that the error messages are used directly as strings. This is not a good idea as changing them might be very difficult. We can define other constants and move them also outside of the function:

let kMinimumLength = 3
let kMaximumLength = 10
let kUsernameTooShort = "Username too short. It needs to be longer than \(kMinimumLength)"
let kUsernameTooLong = "Username too long. It needs to be shorter than \(kMaximumLength)"
let kUsernameValid = "Username valid"
let kUsernameCannotBeNil = "Username cannot be nil"

func validateUsername(username:String?) {
    if let username = username {
        let length = username.characters.count
        if length <= kMinimumLength {
            print(kUsernameTooShort)
        } else if length >= kMaximumLength {
            print(kUsernameTooLong)
        } else {
            print(kUsernameValid)
        }
    } else {
        print(kUsernameCannotBeNil)
    }
}

No comments:

Post a Comment