Wednesday, March 16, 2016

Hide field

All the fields and methods should be private to start with.

In Swift, they are three levels of access levels
- private
- internal
- public

Private and public are obvious. Internal access means public access for the current module, but private for everything else.

The default is the internal access, which means public if we are working with just one module.

But, of course, that is not a good idea. Every class should try to hide as much as it can from the other classes.

One way to achieve this is to make everything private to start with and only change it if need be.

Here is the class used in the previous post:

class Account {
    var isCheckingAccount: Bool = false
    func isChecking() -> Bool {
        return isCheckingAccount
    }
}

We notice that isCheckingAccount is never accessed directly. So we can go ahead and make it private:

class Account {
    private var isCheckingAccount: Bool = false
    func isChecking() -> Bool {
        return isCheckingAccount
    }
}


Tuesday, March 15, 2016

Replace subclasses with fields

The subclasses need to have a reason for existence. A very good reason.

When that reason disappears or becomes slim, it might be time for removing the subclass.

Let's consider the following hierarchy of classes:

class Account {
    func isChecking() -> Bool {
        return false
    }
}

class CheckingAccount: Account {
    override func isChecking() -> Bool {
        return true
    }
}

class SavingAccount: Account {
    override func isChecking() -> Bool {
        return false
    }
}

While at the beginning the CheckingAccount and SavingAccount were different in functionality, in time, the only difference between them remained just the isChecking method that returns true for one and false for another.

We can refactor this structure, by adding one property to the Account class to reflect if the account is checking or not.

class Account {
    var isCheckingAccount: Bool = false
    func isChecking() -> Bool {
        return isCheckingAccount
    }
}



Monday, March 14, 2016

Replace parameter with method

Apply this method when the code is invoking one function and passes the result to another function.

If the second function can invoke the function by itself, it should.

Let's consider the following class:

class User {}

And a function that retrieves the current user:

func getCurrentUser() -> User {
    var currentUser: User
    //find the user
    return currentUser
}

The following function returns all the friends of a user:

func findFriendsForCurrentUser(user:User) -> Array<User> {
    var array = [User]()
    //uses user to find his friends
    return array
}

We can invoke the functionality as follows:

let currentUser = User()

let currentUserFriends = findFriendsForCurrentUser(currentUser)

We notice though, that we always use the functionality for the current user.

So there is no point in passing the current user, when the function can get it by itself.

We remove the parameter and change the function to get the current user by itself:

func findFriendsForCurrentUser() -> Array<User> {
    let user = getCurrentUser()
    var array = [User]()
    //uses user to find his friends
    return array
}


Friday, March 11, 2016

Rename method

When to rename a method or a function?

But before that, do you know what is the difference between a method and a function?

Well a method is a married function :-)

Joking, a method is a function that is associated (for life) with another class,. So a method is a class function.

Back to our refactoring.

We need to rename a function in one of the following cases:

- the previous developer picked a name so bad that nobody understand what it is about. Remember the name should tell us everything we need to know about its purpose

- the purpose of the function changed

- the return type changed. For example if getUser is now returning an Account, of course it needs to be changed to getAccount

- the name is not following the coding standards the team adopted.

Finding a good name is not easy. But working with bad names is a pain, so the effort is paying off.

Read more here How to name your variables and your kids

Thursday, March 10, 2016

Refactoring - Remove the paramenter

This refactoring is so simple, we'll not even give an example to prove it.

With functionality change, some of the parameters of a function become obsolete and not used.

When that is the case, of course, they need to be removed.

Easier said than done in some cases, especially when the functions are part of a library used by other parties.

In that case, we can keep the function with the extra non used parameter, deprecated it, and create another function with the correct parameters.

At least the new users of the function will know to use the correct version.

Let's assume the following function:

func testFunction(param1:Int, param2: Int, unusedParam: Int) {
}

After few iterations, unusedParam is not used anymore. We can remove it right away, but this will break few other programs that are using our function. 

So instead of doing that, we can mark it as deprecated and create a new, refactored version as follows:

@available(*, deprecated=1.0, message="unusedParam is not used anymore")
func testFunction(param1:Int, param2: Int, unusedParam: Int) {
    testFunction(param1, param2: param2)
}

func testFunction(param1:Int, param2: Int) {
    //move here the code from the previous function
}


Wednesday, March 9, 2016

Refactoring: remove the named parameters

The functions in Swift can have named or unnamed parameters. Usually, named parameters give more clarity on their purpose. Sometime though, the names are redundant. In those cases, the names can be removed.

For example, in the following cases:

func max(firstValue firstValue:Float, secondValue:Float) -> Float {
    if firstValue < secondValue {
        return secondValue
    } else {
        return firstValue
    }
}

func powerOfTwo(power power:Int) -> Float {
    if power < 0 {
        return 1 / powerOfTwo(power: -power)
    }
    if power == 0 {
        return 1
    }
    return 2 * powerOfTwo(power: power-1)
}

We can invoke the functions as follows:

max(firstValue:10, secondValue: 20)

powerOfTwo(power: 10)

The parameters' names are not bringing any value to the code. It is obvious that the first one is calculating the maximum between two numbers. The order of the numbers does not matter.

The second one is calculating the power of two. The parameter is the power and that is obvious.

We can remove the parameter names, making the code simpler:

func max(firstValue:Float, _ secondValue:Float) -> Float {
    if firstValue < secondValue {
        return secondValue
    } else {
        return firstValue
    }
}

func powerOfTwo(power:Int) -> Float {
    if power < 0 {
        return 1 / powerOfTwo(-power)
    }
    if power == 0 {
        return 1
    }
    return 2 * powerOfTwo(power-1)
}

In this case, we can call the functions as follows:

max(10, 20)
powerOfTwo(10)


Tuesday, March 8, 2016

Refactoring - remove control flag

Many times when there are a series of conditional tests, we use a flag to keep a temporary boolean value.

If this is happening inside a function we can replace this flag with return or exit statement.

Have a look at the following example:

class User {
    var firstName = ""
    var middleName = ""
    var lastName = ""
}

We also have a database of users:

var userArray = [User]()

And here is a search function for a user that has a specific name:

func searchName(name:String) -> User? {
    var found = false
    var user:User?

    for var index = 0; (index < userArray.count) && !found; ++index {
        
        let crtUser = userArray[index]
        
        if crtUser.firstName == name {
            user = crtUser
            found = true
        }
        
        if crtUser.middleName == name {
            user = crtUser
            found = true
        }
        
        if crtUser.lastName == name {
            user = crtUser
            found = true
        }
    }
    return user
}

So many lines for a simple search.

We can eliminate the variable and instead of storing locally the user, we can just return it as follows:

func searchName(name:String) -> User? {
    
    for var index = 0; index < userArray.count; ++index {
        
        let crtUser = userArray[index]
        
        if crtUser.firstName == name {
            return crtUser
        }
        
        if crtUser.middleName == name {
            return crtUser
        }
        
        if crtUser.lastName == name {
            return crtUser
        }
    }
    return nil
}

We can continue the refactoring by combining all the tests in one:

func searchName(name:String) -> User? {
    
    for var index = 0; index < userArray.count; ++index {
        
        let crtUser = userArray[index]
        
        if (crtUser.firstName == name) || (crtUser.middleName == name) || (crtUser.lastName == name) {
            return crtUser
        }
    }
    return nil
}

Also we notice that a for-in might work better and the code look more elegant:

func searchName(name:String) -> User? {
    
    for crtUser in userArray {
        
        if (crtUser.firstName == name) || (crtUser.middleName == name) || (crtUser.lastName == name) {
                return crtUser
        }
    }
    return nil
}


Coding standards - of galactic importance


Monday, March 7, 2016

Refactoring - Push down field or method

This refactoring is applied when there is a field or method in a superclass that is used only in one of the subclasses.

One class should have the information it needs to have, so it makes sense to push down that field or method to the subclass where it belongs.

Here is an example:

class BankAccount {
    var overdraft: Float = 0
    
    func enableOverdraft(overdraft: Float){
    }
}

class CheckingAccount: BankAccount {
}

class SavingAccount: BankAccount {
}

BankAccount has the overdraft, but, according to the business rules, an overdraft applies only to a checking account. In this case we can move the functionality to the CheckingAccount class.

class BankAccount {
}

class CheckingAccount: BankAccount {
  var overdraft: Float = 0
    
    func enableOverdraft(overdraft: Float){
    }
}

class SavingAccount: BankAccount {
}

By doing this, not only we move the functionality to the place where it belongs, but we also eliminate the risk to have this functionality called for the incorrect object.

For example, in the first case, the overdraft functionality could have been called for a saving account, which is incorrect.

Friday, March 4, 2016

Refactoring - pull up method

This refactoring is applied when two classes have an identical method. It can be moved up to the parent.

Let's continue the example from previous post:

class BankAccount {
    var balance : Float = 0
}

class CheckingAccount: BankAccount {
    func displayAlert(){
        if balance < 0 {
            print("You have a negative balance of \(balance).")
        }
    }
}

class SavingAccount: BankAccount {
    func displayAlert(){
        if balance < 0 {
            print("You have a negative balance of \(balance).")
        }
    }
}

We have just refactored the balance field, but we noticed we also have a method (displayAlert) that is identical. We can apply the same refactoring and move it up the hierarchy.

class BankAccount {
    var balance: Float = 0
    
    func displayAlert(){
        if balance < 0 {
            print("You have a negative balance of \(balance).")
        }
    }
}

class CheckingAccount: BankAccount {
}

class SavingAccount: BankAccount {
}



Thursday, March 3, 2016

Refactoring - Pull up field

This is a simple refactoring applied when there are two subclasses with a common field. Take the field and move it to the parent class.

Consider the following class structure:

class BankAccount {
}

class CheckingAccount: BankAccount {
    var balance: Float = 0
}

class SavingAccount: BankAccount {
    var balance: Float = 0
}

The common balance field can be moved to the parent:

class BankAccount {
    var balance: Float = 0
}

class CheckingAccount: BankAccount {
}

class SavingAccount: BankAccount {
}

This is less code to write and some common functionality can be also moved up.

The purpose of this refactoring is to have as little duplication as possible,

Wednesday, March 2, 2016

Refactoring - Pull up constructor body

As a rule of thumb, each time you see the same code in different places, look for ways to remove the duplication.

This refactoring applies when two or more subclasses have similar code in the init method. We can move that code in the superclass.

Have a look at the following example:

class Vehicle {
    var make: String?
    var model: String?
    var mileage: Float?
    var maximumSpeed: Float?
    var horsePower: Float?
    var colour: UIColor?
    
    init() {
        //...
    }
}

class Car: Vehicle {
    var numberOfDoors: Int?

    init(make: String, model: String, mileage: Float, maximumSpeed: Float, horsePower: Float, colour: UIColor, numberOfDoors: Int) {
        super.init()
        self.make = make
        self.model = model
        self.mileage = mileage
        self.maximumSpeed = maximumSpeed
        self.horsePower = horsePower
        self.colour = colour
        self.numberOfDoors = numberOfDoors
    }
}

class Motorcicle: Vehicle {
    var numberOfWheels: Int?

    init(make: String, model: String, mileage: Float, maximumSpeed: Float, horsePower: Float, colour: UIColor, numberOfWheels: Int) {
        super.init()
        self.make = make
        self.model = model
        self.mileage = mileage
        self.maximumSpeed = maximumSpeed
        self.horsePower = horsePower
        self.colour = colour        
        self.numberOfWheels = numberOfWheels
    }
}

I marked with bold the duplicated code. To apply this refactoring, we move the common code into the superclass, removing a good few lines of code:

class Vehicle {
    var make: String?
    var model: String?
    var mileage: Float?
    var maximumSpeed: Float?
    var horsePower: Float?
    var colour: UIColor?
    
    init(make: String, model: String, mileage: Float, maximumSpeed: Float, horsePower: Float, colour: UIColor) {
        self.make = make
        self.model = model
        self.mileage = mileage
        self.maximumSpeed = maximumSpeed
        self.horsePower = horsePower
        self.colour = colour
    }
}

class Car: Vehicle {
    var numberOfDoors: Int?
    
    init(make: String, model: String, mileage: Float, maximumSpeed: Float, horsePower: Float, colour: UIColor, numberOfDoors: Int) {

        super.init(make: make, model: model, mileage: mileage, maximumSpeed: maximumSpeed, horsePower: horsePower, colour: colour)

        self.numberOfDoors = numberOfDoors
    }
}

class Motorcicle: Vehicle {
    var numberOfWheels: Int?
    
    init(make: String, model: String, mileage: Float, maximumSpeed: Float, horsePower: Float, colour: UIColor, numberOfWheels: Int) {

        super.init(make: make, model: model, mileage: mileage, maximumSpeed: maximumSpeed, horsePower: horsePower, colour: colour)        
        self.numberOfWheels = numberOfWheels
    }
}



Tuesday, March 1, 2016

Refactoring - Preserve whole object

You can use this refactoring when your code is getting few properties from an object and then pass them to a function. You can simply pass the whole object.

Let's consider a User class that has among other properties, a first name and a last name.

class User {
    var firstName: String
    var lastName: String
}

We have a function that creates the header for a report. It is using the two values like follows:

func createHeader(firstName firstName:String, lastName:String) {
    //...
}

To use the code we need to do the following

let user = User()
let firstName = user.firstName
let lastName = user.lastName

createHeader(firstName:firstName, lastName: lastName)

We observe that the firstName and lastName variables are just temporary ones that do not bring any value.

One first step would be to remove them in the following way:

let user = User()
createHeader(firstName:user.firstName, lastName: user.lastName)

But we can do even better than that. We can change the function to accept the User object altogether:

func createHeader(user: User) {
    //...
}

If in the future, we want the add other information to the header, for example user's phone number, the change is a piece of cake.