Refactoring For Clean Code With Design Patterns

Bora Kaşmer
The Startup
Published in
13 min readMay 1, 2020
Image Source: HOLY BATH

Hi,

Today we will talk about the clean code. Is it really necessary? If our codes look like messy, what is waiting for us in the future? Does clean code mean only, easy to read, and simplicity? Or much more than this?

There is, after all, a difference between code that is easy to read and code that is easy to change.
— “Big” Dave Thomas

First of all, I know, nobody has so much time. Boss waits for the see of finalizing the project. He or she doesn’t care about the details. You can think your self as a butcher in front of the customer. Boss is your customer, and you are the butcher. The customer doesn’t care about your hygiene. He only wants to take his meat as possible as fast. So the boss doesn’t want you to wash your hands for every customer. Why, because time is money. Finally, if you don’t wash your hands and not waste your time(unlike this example, you always wash your hands, please!), maybe at the end of the day, everyone could be happy except you :) But in the future, customers could be sick and sue the company. In the end, nobody will be happy. Boss doesn’t know the details. Only you know the risks. So take all responsibility and clean your code as possible.

We will refactor very messy AI application codes at the end of this article. But first, we have to understand these three design pattern strategies, which we will use for refactoring that messy codes at the end. Maybe most of you have already known these, but there is no harm in repeating a good thing :) In short, the next two design patterns will prepare you for the final example. And we will examine the last one in-depth within this messy AI application. Just like in business training :)

You have to avoid the repeatable codes…

In the C# Console App code below, you can see the multiple business logic. This is against the first rule of S.O.L.I.D.

Single Responsibility. A class should have one, and only one, a reason to change. Why, because to easy read, to easily test, and for simplicity. Look at that code; every logic has its own business. CRM, Finance and Service. Software is not an All In One Campaign. It is a distributed process. If you want to change the CRM process, all the applications, which are used in this class, are affected by this update. And for one business changing, you have to rebuild all other processes. This means, for the “CRM” process changing, “Finance” and “Service” must-stoped. Using enum is the only good thing in this code :)

Can you feel the other lousy smell in this code? In the future, if you need one more process, you should find this class and add another “if condition.” It is insane and not sustainable. If another ten new logic comes to this class, the code becomes unreadable.

Firstly, let’s separate all the processes to the different classes. And all the classes must be inherited from another class or interface. The naked class is an undesirable thing in object-oriented programming.

Image Source

IBussineLogic :

All business logic must be inherited from the same interface (IBussinesLogic). And all classes should override the same “WorkProcess()” method. Do you remember this Design Pattern?

using System;
namespace SolidStrategy
{
public interface IBussinesLogic
{
void WorkProcess(string message);
}
}

Crm:

The first business class is CRM. It is inherited from “IBusinessLogic.” That is the reason why the “WorkProcess()” method must be implemented. Every “WorkProcess()” method, belongs to the different parent class. So all of them have different logic.

Crm Class :

using System;
namespace SolidStrategy
{
public class Crm : IBussinesLogic
{
public void WorkProcess(string message)
{
Console.WriteLine($"Process Crm! :{message}");
}
}
}

Finance:

The second business class is Finance. It is inherited from the “IBusinessLogic,” like CRM. And also, It has to implement the “WorkProcess()” method for different Financial logic.

using System;
namespace SolidStrategy
{
public class Finance : IBussinesLogic
{
public void WorkProcess(string message)
{
Console.WriteLine($"Process Finance! :{message}");
}
}
}

Service:

The last business class is Service. Like other business classes, it is inherited from “IBusinessLogic”. It also has to implement the “WorkProcess()” method for different Service logic.

using System;
namespace SolidStrategy
{
public class Service : IBussinesLogic
{
public void WorkProcess(string message)
{
Console.WriteLine($"Process Service! :{message}");
}
}
}

Process:

Now we have to cover all business logic in one container. Why, because customers, in this case, actually other developers should only be interested in one class. Because simplicity is everything. “And if a new business logic comes to the town, nobody should have to change this main Process code” :)
The Process class requires a logic class inherited from “IBussinesLogic” in Constructor().

What is the common feature of all classes? Of course “WorkProcess()” method. When the “WorkProcess()” method is called from the Process class, the logic differs depending on the “IBussinesLogic” class received from the Constructor.

using System;
namespace SolidStrategy
{
public class Process
{
IBussinesLogic _bussines = null;
public Process(IBussinesLogic bussines)
=> _bussines = bussines;
public void WorkProcess(string message)
{
_bussines.WorkProcess(message);
}
}
}

Strategy Design Pattern is the answer to the above question. It is a behavioural software design pattern that enables selecting an algorithm at runtime. We used this pattern for getting clean and simple code in this situation.

Try and leave this world a little better than you found it . . .
— Robert Baden-Powell

Program.cs

Messy Codes are becoming this a few lines of code. Let’s play with the new Toy :)

Simplicity is everything — much more readable code. But there is one a more important thing :
“You can easily add new logic without changing any line of code. And nobody is affected by this change.”

Program.cs/Main() :

using System;namespace SolidStrategy
{
class Program
{
static void Main(string[] args)
{
Process process = new Process(new Crm());
process.WorkProcess("Update Crm DB");
}
}
}

This is the result screen :

Extraction Methods

Our second scenario is about the Extraction method. Extraction is my favourite Refactoring method.

With the extract method, you move a fragment of code from an existing method into a new method. And please don’t forget to give a name about what it is doing. This technique is used to escape from complexity and improve the readability of the code.

Finding bugs in a mass is like looking for a needle in a haystack. Divide that mass and find all errors piece by piece. And never forget that if you divide it, you can share it
— Bora Kaşmer

Look at the below code, everything is in the one place, and all the logics are working together. We will find all different logics and extract them from this method into different methods. The purpose is better readability and simplicity.

Lets check the “CalculateSallaryIncrease()” method. We have three jobs in this method.

1-) Find salary raise rate by departments:

This code returns the salary increase rate depending on the department of the person. The name is important. You can understand the purpose of this method from its name.

public static double GetSalaryRatebyDepartment(Department department)
{
switch (department)
{
case Department.IT:
{
return 1.1;
}
case Department.HumanResource:
{
return 1.2;
}
case Department.System:
{
return 1;
}
case Department.Officer:
{
return 1.3;
}
}
return 1;
}

2-)Let’s calculate the new salary of the employee by using the new “raise rate,” which found with the above code.

You can see the order of the parameters is understood from the name of the method. Calculate => NewSallary=>WithRate (salary, rate). In the future, new business logic can be easily added to this method for every person’s salary calculation.

public static double CalculateNewSallaryWithRate(double salary, double rate)
{
return salary * rate;
}

3-) Append the tag to a person name by gender. Mr. or Ms.

Why did we separate this method? Because if someone wants to add a new tag for any conditions in the future, they should be able to easily add it to this method without any extra effort.

public static string AppendTagToNameByGender(string name, Gender gender)
{
return gender == Gender.Male ? $"Mr.{name}" : $"Ms.{name}";
}

Now our new “CalculateSalaryIncrease()” method looks like this. More readable, more understandable, and, most importantly, much more “short.”

public static void CalculateSalaryIncrease(ref List<Person> personList)
{
foreach (Person person in personList)
{
double sallaryRate = GetSalaryRatebyDepartment((Department)person.Department);
person.Salary = CalculateNewSallaryWithRate(person.Salary, sallaryRate); person.Name = AppendTagToNameByGender(person.Name, person.Gender);
}
}

This is the result screen :

This is the final and main example of this article:

It is tried to be understood with specific analysis rules, whether the three comments(Description1, Description2, Description3) made in the example below belong to a woman or a man.

Image Source

Of course, it is not a real-life scenario. But it is helping us to understand clean code. We have two groups of word libraries. Words that Men and Women often use when speaking. We try to decide whether the sex of the speaker is male or female, by refining these words or a group of words in the comment made.

For men, we are searching for (football or car) words in the description. If one of these words appears in the interpretation, we are accepting that the person who said it was a man.

For women, we are searching for (mother or baby) or (draw and car) or (rub and car) words in the description. If one of these words or word groups appears in the interpretation, we are accepting that the person who said it was a woman.

What is wrong with this code?

. This code is very hard to understand without any description.

. Code Readability is awful.

. If any new rules come, we have to modify all conditions.

. After a while, following the and — or conditions is impossible.

. Finally, this code is not written for the human to understand. It is written for the computer to understand :)

A scene from the HBO WestWord series

Interpreter Design Pattern

To clean this mess, we will use a couple of design patterns. And of course, the main pattern for this solution will be the “Interpreter Design Pattern.”

The interpreter is a behavioural design pattern. It is used to evaluate language grammar or expression, which is implemented from an Interface. In this application, our expression is these three descriptions. This pattern uses these expressions interfaces for interpreting a particular context.

Now we are going to create an interface Expression. Later, we will create the And — Or classes by implementing this Expression interface. “Or” Expression and “And” Expression is used to create combinational expressions. And of course, we will create a “TerminalExpression” class, which is defined as acts as the main interpreter of context in the description. In this application, we call it “CheckExpression.”

“In the future, if new rules would come for detecting the gender of a commenter, we may have to create a new kind of Expression.”

1-) Let’s create Expression Interface: All other expression classes must use this “Interpret()” method.

public interface Expression
{
bool Interpret(string content);
}

2-) Create CheckExpression: We will take a word from the constructor and then check if the related word is contained in the content (description), which is received as a parameter in the Interpret method or not. This will be our first tool. And we will use it everywhere.

Image Source

CheckExpression Class :

public class CheckExpression : Expression
{
private string word;
public CheckExpression(string _word)
{
this.word = _word;
}

public bool Interpret(string content)
{
return content.ToLower().Contains(word.ToLower());
}
}

3-) Create OrExpression: This is our second tool. We will use the above Expression class. We need two of expression classes in this method. The expression means “word” in this application. We will check “one” of these words is contained in this description or not.

Image Source

OrExpression Class :

public class OrExpression : Expression
{
private Expression exp1;
private Expression exp2;
public OrExpression(Expression _exp1, Expression _exp2)
{
this.exp1 = _exp1;
this.exp2 = _exp2;
}
public bool Interpret(string content)
{
return (exp1.Interpret(content) || exp2.Interpret(content));
}
}

If you pay a little attention, you can see that we are using another tool to build a new tool! It is like use robot to create a new robot :)

A secene from “Ex Machine” Movie
Image Source

4-) Create AndExpression: This is our last tool. Again here we need two of expression classes. We will check if “both” of these words are contained in this description or not.

AndExpression Class :

public class AndExpression : Expression
{
private Expression exp1;
private Expression exp2;
public AndExpression(Expression _exp1, Expression _exp2)
{
this.exp1 = _exp1;
this.exp2 = _exp2;
}

public bool Interpret(string content)
{
return (exp1.Interpret(content) && exp2.Interpret(content));
}
}

What have we done by creating these three tools?

1-) We divided that mass code. We moved two fragments of code from existing methods into new methods(AndExpression, OrExpression). We took the work a little further more, we moved one fragment of code from these methods into new methods too (CheckExpression). Do you remember that?

We call that “Extract Method,” which we have already talked about at the beginning of the article.

2-) I want to draw your attention to the “AndExpression and OrExpression” classes. Both inherit from the “Expression” interface. Both have the same “Interpret()” method. See the code below. “getFemailExpression()” has an Expression List. It can take any class that is inherited from the “Expression” Interface. Like “AndExpression,” “OrExpression”. This means that “getFemailExpressions()” chooses an algorithm at the run time. Do you remember that?

We call that “Strategy Design Pattern,” which we have already talked about at the above of the article.

InterpretPattern Class :

. CheckExpression: It is a Word.
. OrExpression — AndExpression: Expression for deciding to the gender of the commentator. It takes one or more “CheckExpression” to decide.
. Interpret(): All Expressions have to implement this method. It takes content(description) as a parameter. And It decides to gender. Man or Woman.

All three tools are created to be used in this class. “AndExpression”, “Expression” and “OrExpression”. There are two situations here. Man or Woman Decision Rules. They are collected and returned as an Expression or List of Expression.

getMaleExpression(): It returns OrExpression. Every expression is a word, and two of them are given as a parameter to the OrExpression class. Every OrExpression is a rule to decide on gender.

getFemailExpression(): It returns a List of Expression. There are three Expression Rules for decided to Woman Gender. Two AndExpression and one OrExpression. All of them takes six expressions (word) as a parameter.

public class InterpretPattern
{
public static Expression getMaleExpression()
{
Expression futbol = new CheckExpression("football");
Expression araba = new CheckExpression("car");

return new OrExpression(futbol, araba);
}
public static List<Expression> getFemailExpressions()
{
List<Expression> ListExpression = new List<Expression>();
Expression mother = new CheckExpression("mother");
Expression baby = new CheckExpression("baby");
Expression rub = new CheckExpression("rub");
Expression draw = new CheckExpression("draw");
Expression car = new CheckExpression("car");
ListExpression.Add(new OrExpression(mother, baby));
ListExpression.Add(new AndExpression(rub, car));
ListExpression.Add(new AndExpression(draw, car));
return ListExpression;
}}

Everything seems to be part of the puzzle. All of them are interlocking, and the small pieces form larger pieces, and larger pieces form the picture.

Photo by Markus Winkler on Unsplash

Look at the below code, much more clear. Implementation is easy. You don’t have to know business logic. “It is not your concern to, which words must contain into the content to decide to the woman.”
If new rules or new words come to the business logic, you don’t have to change all the codes. This is the nature of OOP Programming.

Formation Tree :
Expressions =>CheckExpression =>OrExpression&AndExpression =>getFemailExpressions&getMaleExpression

In case the rules are run one by one according to the business logic, if one of the rules passes the specific conditions, all the processes are stopped and returned to the “true” result.

This is the result screen :

In this article, we talked about how could convert unreadable messy code to explicit and clarity code by using design patterns. In some cases, only one design pattern doesn’t solve your problem. In this case, you should prefer more than one design pattern for the solution.

OOP programming and Design patterns are optimizing your code for easy maintenance ( change and testing ), make them extendable and flexible.

Image Soruce

Quick Tip: Imagine a new rule coming to the “getMaleExpression()” to identify the male commentator. For this scenario, three expressions (words) should be contained in the content (description). This is something completely new.

Creating new kind of Expression like below is enough. It is named “And3Expression”. We are implementing from Expression interface, and this time we are creating three Expression(words). Finally, on “Interpret()” method, we are checking these three words are contained in the content (description) or not. That’s all. We don’t need to change anything in anywhere. Just add this new “And3Expression” class and use it in the “getMaleExpression()”. This is the power of OOP programming.

public class And3Expression : Expression
{
//bag,shoe,hairdresser
private Expression exp1;
private Expression exp2;
private Expression exp3;

public And3Expression(Expression _exp1, Expression _exp2, Expression _exp3)
{
this.exp1 = _exp1;
this.exp2 = _exp2;
this.exp3 = _exp3;
}
public bool Interpret(string content)
{
return (exp1.Interpret(content) && exp2.Interpret(content) && exp3.Interpret(content));
}
}

“If you have read so far, first of all, thank you for your patience and support. I welcome all of you to my blog for more!”

Source Codes: https://github.com/borakasmer/CleanCode
Sources:
refactoring.com, geeksforgeeks.org, en.wikipedia.org

--

--

Bora Kaşmer
The Startup

I have been coding since 1993. I am computer and civil engineer. Microsoft MVP. Software Architect(Cyber Security). https://www.linkedin.com/in/borakasmer/