
Helpful Clean Coding Tips
I will try to mention some software design topics which I think are important. These are:
- Law of Demeter
- Single Responsibility Principle
- Composition over Inheritance
- Primitive Obsession
- Listen to Tests
- Use Dependency Injection Technique
Law of Demeter(LoD)
LoD is a design guideline for developing software whose rules and benefits can be summarized as:
Rules (based on Wikipedia):
- Each class must be able to access to another class only the information and resources that are necessary for its legitimate purpose.
- Each unit should have only limited knowledge about other units.
- Each unit should only talk to its friends; don’t talk to strangers.
Benefits:
- It gives full control over class properties. You cannot change any property from outside of its class context.
- It makes loose coupling relations over classes.
- It makes it easier to refactor code.
Let’s think about that we are working on a client application. The classes in the project are as follows:
I only want to add NumericFilter
to the PasswordView
. To make this, I have to add the filter to EditableTextView
and have to know how EditableTextView
keeps filters.
- All of this information creates an unnecessary dependency between
Screen
andEditableTextView
. Thus these classes are coupled now. Whenever we want to changePasswordView
orEditableTextView
filter mechanism, we must update theScreen
class also. - How can we add restriction to the functionality of adding a filter to
PasswordView
orEditableTextView
? For example, how can I prevent addingAlphanumericFilter
toPasswordView
? As you can see we have no control over filters.
How must we implement these classes?
- I don’t need any knowledge about
EditableTextView
to usePasswordView
. - I can have full control over adding filter.
- Any changes to
EditableTextView
implementation do not affectPasswordView
api.
Single Responsibility Principle(SRP)
What does responsibility mean in the SRP concept? Assume that we need a telephone class. While we creating it, we want to stick by the SRP. That’s why let’s start with the writing responsibilities of a telephone.
- Must be able to send text messages.
- Must be able to make a call.
- Must be able to connect to the internet.
…
It goes like this. As you can see, there are many responsibilities. So how can we stick to the SRP in the presence of a lot of responsibilities? Could we be making a mistake when defining the concept of responsibility?
Let’s look at Robert C. Martin’s definition:
Martin defines a responsibility as a reason to change,
and concludes that a class or module should have one, and only one, reason to be changed (e.g. rewritten).
It means implementations of each responsibility(or we can call this duty) must be defined in somewhere else.
In case these functions need to be changed, we have to refactor the Telephone
class. That’s it, it violates the SRP. To be compatible with the SRP, we have to construct Telephone
class as follows:
Any of VoiceSignalManager
, TextMessageSignalManager
, or NetworkManager
implementation changes do not affect to Telephone
class. So our Telephone
class is compatible with the SRP.
All of these are eligible to function definitions as much as class.
Composition over Inheritance
How can we decide to choose between composition or inheritance? Which one is the best practice? It is quite simple, the answer relies on this question, which kind of relationship is there between classes, is there a “has-a” relationship or an “is-a” relationship? If the answer is there is an “is-a” relationship then the best practice is inheritance, otherwise composition.
- What is an “is-a” relationship? A taxi is a vehicle also a bus is a vehicle too. (inheritance)
- What is a “has-a” relationship? A taxi has an engine. (composition)
It seems very easy to decide but in practice it’s not that easy. Many times we go in the wrong direction. At which point do we make the mistake?
Let’s continue with another example. Imagine we are working on a client application that has different screens.
There is TextInputView class:
Then let’s add a day and night theme to it now.
Is there something wrong with the class structure above? If there is then what is it? Let’s answer the “is-a” and “has-a” questions.
- Are
NightThemedTextInputView
andDayThemedTextInputView
aThemedTextInputView
? Yes. - Is
ThemedTextInputView
aTextInputView
? Yes. - Is
Theme
is a TextInputView? No. - Is
DayTheme
is aThemedTextInputView
? No. - Is
NightTheme
is aThemedTextInputView
? No. - Has
TextInputView
aTheme
? No.
Here starts our mistake…
- Has
ThemedTextInputView
aTheme
? Yes. - Has
DayThemedTextInputView
aTheme
? Yes. - Has
NightThemedTextInputView
aTheme
? Yes.
If ThemedTextInputView
, DayThemedTextInputView
and NightThemedTextInputView
all have Theme
then why we inherit from ThemedTextInputView
to construct NightThemedTextInputView
and DayThemedTextInputView
? Even if NightThemedTextInputView
and DayThemedTextInputView
are a ThemedTextInputView
, we cannot inherit Theme
attribute. This is the trap which is seen many times. In order not to fall into this trap we need to know we cannot inherit a function that has a “has-a” relationship.
So ThemedTextInputView
should be like this:
Primitive Obsession
It shortly means leaving type safety out. Credit card, email, date, password, URL and many others. This kind of information mostly stored as String. All kinds of logic which are related to all of this information mostly put in Utility classes.
Alright, why is putting this kind of information in Utility classes wrong, what is the best practice in this situation? Let’s think about date information.
Date information could be stored in many forms, such as only day, just month or whole date. It could be some part of the whole date, or complete. It could have different formats too. Can we understand all of this information just by looking at the variable name?
var creditCardExpirationDate: String
If you know about credit cards, you can easily say that the “creditCardExpirationDate” variable has to store month and year. But can you tell what its format is, is it “11/2024” or is it “11/24”? So this is not type-safe. Therefore we need to create CreditCardExpiryDate
which is tailored for this purpose.
If we are working on a date, there must be a Date class. This kind of information has more attributes than primitives thus we have to define them as a type.
Listen to Tests
If writing a test is difficult, then you must think twice about your class structure. The cause is most likely because it violates one or more software concept(this is often the “single responsibility” rule). This tells us writing unit test is the best teacher about clean coding.
Use Dependency Injection
Use dependency injection to make it easier to apply the rules and techniques above to your project.
- It makes circular dependencies visible.
- It makes the classes loosely coupled.
Use dependency injection framework.
- It reduces boilerplate code
- It makes class creations easier.