Refactoring & Design Pattern — Improve your Code Quality
This article is written as a part of Individual Review of Fasilkom UI’s Software Engineering Project Course 2021
Isn’t my code good and clean enough?
You might think that you’ve successfully built your code and it is working properly. Your team’s performance is very good and your customer is happy with your team’s result. I challenge you to extend or add new features on the existing code in a few months or years later. Can you easily implement or add it?
In this article, I’ll talk about Refactoring and Design Patterns that you may apply to improve your code quality. As you think, that Software Engineering isn’t only about solving problems, but also about how we can maintain our code in the future.
Refactoring
Refactoring is a systematic process of improving code without creating new functionality that can transform the old code into brand new clean code. In the simple way, we are changing our code without changing the behaviour. You might’ve think now that Refactor is a good practice in Software Engineering to improve our code quality, based on the challenge question in the previous section (to extend or add new functionality).
One simple question will come into our mind, when do we refactor? Here are a simple best practices about when to refactor from refactoring.guru:
Rule of Three
- When you’re doing something for the first time, just get it done.
- When you’re doing the same thing for the second time, you might’ve think that this code can just be implemented once, but you implemented for the second time anyway.
- Then, you’re forced to do the same thing for the third time, it is the right time for you to refactor those things.
Let’s see an example from my Software Engineering Project Course 2021.
I had implemented a feature to update Distributor or Customer’s profile, and the code are as below:
Then, I have to implement this feature in both Admin’s side and Distributor’s side, because Admin can also edit Distributor’s profile. The difference is Admin can edit all registered Distributor’s profile just by specifying the id of the Distributor. On the other side, one Distributor can only edit their own profile. Instead of replicating this code, I extracted this code into a new function, and then call it from both Admin’s and Distributor’s side.
When adding a feature
Suppose there is already a code that do some functionality. You want to add new functionality by extending the current functionality, because the most of the procedure is same with the current code. Refactoring helps you understand other people’s code. If you have to deal with someone else’s dirty code, try to refactor it first.
Let’s see an example from my Software Engineering Project Course 2021.
There is a feature to create a transaction when User clicks buy button on their Cart to processes all the products in the Cart and record them in a transaction object. After that, there is a new feature called buy now which doing the same thing as previous feature, but only with one product per transaction record. Before I refactored the code, the code can only serves to create a transaction from Cart.
As you can see, this is a one big function that do many things, which isn’t good in the future if we want to extend it. Moreover, this function is only exposed as an internal function in one class. So, I refactored the code into smaller functions and transform it into a more reliable Facade Pattern (we will discuss about this Design Pattern later), and made it accessible from other classes.
Because the buy now feature only differs in the number of products, I can now extend the previous refactored code to accepts array ‘Cart’ like object which contains only one product, and pass it into the refactored function.
When fixing a bug & During Code Review
Imagine you’ve a massive code which isn’t really clean, you might have a hard time to discover the bug. By refactoring it, you will automatically discover what’s wrong in your code.
Let’s see an example from my Software Engineering Project Course 2021.
There is another one big function to change transaction’s payment method and the uploaded receipt. By refactoring the code into smaller functions, our team can discover a bug that a receipt and payment method’s shouldn’t be a empty string, so we need to handle it. This bug is also discovered by my team during code review.
Design Patterns
Now, you might have a clear understanding about how refactoring improves our code quality. There is another good practice to improve your code, Design Patterns. Design patterns are typical solutions to common problems in software design. It is like a pair of (problem, solution) that you can use like a blueprint, so you can customize to solve a particular design problem in your code.
Why do we need Design Patterns? Because they are a toolkit of tried and tested solutions to common problems in software design. With Design Pattern, you can easily communicate with other Software Engineers, because this is the standard way to solve a particular problem.
Let’s say, you built a Timer application that should be counted same when running on different windows or tabs, you can propose this as a Singleton Pattern and everyone in your team will understand your idea without explaining what Singleton Pattern is.
There are three classifications of patterns:
- Creational patterns provide object creation mechanisms that increase flexibility and reuse of existing code.
- Structural patterns explain how to assemble objects and classes into larger structures, while keeping the structures flexible and efficient.
- Behavioral patterns take care of effective communication and the assignment of responsibilities between objects.
Note that we mustn’t force to implement Design Patterns if there isn’t any that suits our needs. They are just a toolkit to help us implement particular problems. Let’s see some example of those patterns with the example from my project at Software Engineering Project Course 2021.
Adapter Pattern
First is Adapter Pattern. Adapter is a structural design pattern that allows objects with incompatible interfaces to collaborate.
In the previous example, we have seen that the buy now feature follows the same procedure as the normal buy feature which takes from Cart. The buy now feature only processes one product in a transaction, which means there is no need to add to the Cart first. Because the current method is implemented to take an array of products in Cart, we create a method that wraps our single product into an array, then pass them to the existing method.
An adapter wraps one of the objects to hide the complexity of conversion happening behind the scenes. The wrapped object isn’t even aware of the adapter.
Composite Pattern
Composite is a structural design pattern that lets you compose objects into tree structures and then work with these structures as if they were individual objects.
The next example is from our Frontend’s side project. In React, we can nest the components and treat them as an individual object. In this example, the AdminHome component contains DailyStatistics, WeeklyIncome, and NewDistributors component, and when we accesses the Admin’s Home Page, we can see them all as a single page.
Observer Pattern
Observer is a behavioral design pattern that lets you define a subscription mechanism to notify multiple objects about any events that happen to the object they’re observing.
My project in Software Engineering Project Course 2021 implements the Admin’s Home Page with real-time mechanism, which means the page should automatically update the view when there is any event. Here in this project, my team implemented it with WebSocket, which gives us a real-time connection from Frontend-Backend.
For example, in the Admin’s Home Page, when there is any change in the weekly income (which means there is at least one new verified transaction), the graph / chart should be updated automatically.
This can be implemented with Observer Pattern, which is the Frontend-side project will subscribe into WebSocket events those are sent from the Backend-side project.
My Thoughts
Design patterns are typical solutions to commonly occurring problems in software design. They are like pre-made blueprints that you can customize to solve a recurring design problem in your code.
Although Design Patterns can solve many common problems, note that Design Pattern is not a specific piece of code, but a general concept for solving a particular problem. So, you must adjust your code to follow the Design Pattern’s guideline to suits your program. And you don’t have to always implement one if there isn’t any suits your problem.
Design Patterns are cool and can be combined with Refactoring techniques. For example, you can refactor the code like the Adapter Pattern example of my project on Software Engineering Project Course 2021 to adjust another need but having same procedures.
Don’t forget to refactor your code to improve your code quality, maintainability, scalability, and many more in the future. I like one of Robert C. Martin’s general rule of clean code: “Boy scout rule: Leave the campground cleaner than you found it.”.
Thank you for reading! Happy Coding!