C# code refactoring - Introduction
Tags: csharp, dotnet, cleancode, refactoring
Published: 2021-01-17 07:44:55

How often do you clean up your code and how often do you do the code refactoring? If you have no idea what is code refactoring, it is a process to change the existing code to be more readable, flexible, maintainable, and extendable without changing the existing functionality. 

![code refactoring](https://dev-to-uploads.s3.amazonaws.com/i/1at9ccnszzydm0t21xl6.png)

## Why code refactoring?

It's easier to explain why we need code refactoring by giving an example. The example below is showing an `Animal` class with a function that will take an `AnimalType` as a parameter. If you pass in `AnimalType.Cat`, it will return you `Miao` and the same to the dog.

```csharp
public class Animal {
    public string Sound(AnimalType animalType) {
        if (animalType == AnimalType.Cat) {
            return "Miao";
        } else if (animalType == AnimalType.Dog) {
            return "Woof";
        }
        return "";
    }
}
```

So what's the problem with this `class`? 

If I need to add 10 more animals' sounds into this class, I need to write 10 if-statements. Like the example below.

```csharp
public class Animal {
    public string Sound(AnimalType animalType) {
        if (animalType == AnimalType.Cat) {
            return "Miao";
        } else if (animalType == AnimalType.Dog) {
            return "Woof";
        } else if (animalType == AnimalType.Bird) {
            return "Chick";
        } // else if () x 7 more...
        return "";
    }
}
```

You will realize that the function will become bloated or some people call it a `fat function`. You might think: ok, it's just a function and I don't find an issue extending it. I just have to keep adding the `else-if` statement and it will work like a charm.

You probably are right when this function is small. What if after the function has been added 100 over sounds into it, and you are told to implement another function called `Fly`? How are you going to handle the new function? Write another `if` statement? How do you make sure that you don't miss an animal type and also implementation for the different animals will have different ways to fly is correctly implemented?

```csharp
public class Animal {
    public string Sound(AnimalType animalType) {
        // sound implementation
    }

    public void Fly(AnimalType animalType) {
        if (animalType == AnimalType.Cat || animalType == AnimalType.Dog) { 
            // this if statement might go wrong, because not every animal can fly.
            throw new InvalidOperationException("This animal cannot fly!!");
        } else if (animalType == AnimalType.Bird) {
            // fly implementation
        } 
    }
}
```

So yea, the above implementation is not sustainable. First of all, the above implementation is hard to read. If I were to find an animal type to change the way it `sound`, I need to take more time to search simply because there are too many `if` statement. The same goes for removing or adding a new animal type.

For the above implementation, you can do it better by creating an abstract `animal` class with a must override `Sound` function. Adding `abstract` to a class to prevent the `Animal` class from being instantiated. At the same time, you can create an animal class by implementing the `Sound` function, and yet you won't miss any animal type that is not implementing the `Sound` function. By doing so, your class will look slicker, thinner, and easier to read.

```csharp
public abstract class Animal {
    public abstract string Sound();
}

public class Cat: Animal {
    public override string Sound() {
        return "Miao";
    }
}

public class Dog: Animal {
    public override string Sound() {
        return "Woof";
    }
}

// same for the rest of the animals

```

You might have a question like this: but I would like to get the animal sound by using animal type. Otherwise, I will use the `if` statement again to instantiate the animal class and the code will be duplicated everywhere. 

You are right, we still need to use the `if` statement to instantiate the class. But we will use a [simple factory design pattern](https://refactoring.guru/design-patterns/factory-method) to encapsulate the implementation. 

```csharp
public class AnimalFactory {
    public static Create(AnimalType animalType) {
        if (animalType = AnimalType.Cat) return new Cat();
        if (animalType = AnimalType.Dog) return new Dog();
        // implementation for other animals...
        throw new InvalidOperationException("Invalid animal type!");
    }
}

// usage
Animal cat = AnimalFactory.Create(AnimalType.Cat);
cat.Sound(); // Miao
```

You will probably ask: how about the `Fly` function. Not every animal can fly, right? 

You are right, not every animal can fly, therefore it's not a good idea to implement the `Fly` function to all the animals. Otherwise, you will have to implement 100x functions if you have 100 types of animals. You probably can create an interface for the `IFlyableAnimal`. This idea is coming from the [SOLID principle - Interface Segregation Principle](digitalocean.com/community/conceptual_articles/s-o-l-i-d-the-first-five-principles-of-object-oriented-design#interface-segregation-principle). See the example below:

```csharp
public interface IFlyableAnimal {
    void Fly();
}

public class Bird: Animal, IFlyableAnimal {
    public override string Sound() {
        return "Chick";
    }
    
    public void Fly() {
        // some fly implementation
    }
}

// usage
Animal bird = AnimalFactory.Create(AnimalType.Bird);
bird.Sound(); // Chick
if (bird is IFlyableAnimal) {
    bird.Fly();
}
```

After looking at the example above, the class becomes thinner and extendable. Whenever we need to create an `AnimalType`, we just have to extend the abstract `Animal` class and if the animal is flyable, we just need to implement the `IFlyableAnimall` interface. The compiler will help to take care of the implementation because if the developer doesn't implement the function, the compiler will show an error. With this design, we are in compliance with the [SOLID principle - open-close concept](https://www.digitalocean.com/community/conceptual_articles/s-o-l-i-d-the-first-five-principles-of-object-oriented-design#open-closed-principle).

## When should you refactor your code?

The best time to refactor your code probably is right after you finish writing a feature. You will have a code review session with your peer and discuss the best way to structure your code.

However, you don't always have the luxury to do code refactoring right after the implementation. QA is awaiting your implementation and the product owner doesn't allow you to get extra time to refactor your code because you need to deliver your implementation with the given time.

But It's ok. You can do the code refactoring when you are told to enhance features in the same piece of code again. From there, you can request more time from the product owner by telling them the current code base requires code refactoring, and hence, you need more time.

## Conclusion

Practicing code refactoring will brings you or your team a couple of benefits. One of them would be the code looks cleaner and easier to read. When the next developer is told to enhance a brilliant feature in the same piece of code, the development will be easier because it's extendable. At the same time, the development speed will be a lot faster.

On the other hand, if the code is not refactored when it's needed, the next developer will be suffering reading the code, resulting in taking more time to read and enhance a simple enhancement. When worse comes to worst, you will find errors here and there simply because the code is not sustainable. 

![code refactoring](https://dev-to-uploads.s3.amazonaws.com/i/za0j11g21mt5wuhvruqp.png)

There's a saying:

> The true professional knows that delivering function at the expense of structure is a fool’s errand. It is the structure of your code that allows it to be flexible.  
> If you compromise the structure, you compromise the future.
The fundamental assumption underlying all software projects is that software is easy to change. If you violate this assumption by creating inflexible structures, then you undercut the economic model that the entire industry is based on.  
> *In short: You must be able to make changes without exorbitant costs.*  
> ~ The clean coder by Robert C. Martin

Last but not least: happy coding and happy learning 😆