DEV Community

loading...
Cover image for C# code refactoring - Introduction

C# code refactoring - Introduction

hongtat profile image Yew Hong Tat ・5 min read

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

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.

public class Animal {
    public string Sound(AnimalType animalType) {
        if (animalType == AnimalType.Cat) {
            return "Miao";
        } else if (animalType == AnimalType.Dog) {
            return "Woof";
        }
        return "";
    }
}
Enter fullscreen mode Exit fullscreen mode

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.

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 "";
    }
}
Enter fullscreen mode Exit fullscreen mode

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?

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
        } 
    }
}
Enter fullscreen mode Exit fullscreen mode

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.

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

Enter fullscreen mode Exit fullscreen mode

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 to encapsulate the implementation.

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
Enter fullscreen mode Exit fullscreen mode

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. See the example below:

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();
}
Enter fullscreen mode Exit fullscreen mode

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.

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

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 😆

Discussion (0)

pic
Editor guide