We’ve all written if/else statements that we are not proud of lol. Sometimes we try to play god in our code and handle everything! This creates if statements that contain endless chains of nested logic.
This is even compounded more by OOP where single if/else is used throughout the entire codebase and is difficult to change once it’s out of control. There are many ways to tame this but let’s start with the most simple.
Extract Method
Check out this real life example from a React project that I made on my Youtube channel.
const checkNumber = (value) => {
if (typeof value === "string") {
if (moment(value).isValid()) {
// Do something
} else {
// Do something else
}
} else if (typeof value === "number") {
if (value > 1000000 || value < -1000000) {
//Do something
} else {
//Do something else
}
}
}
This code is very easy to understand and all it does is check whether value is a string or number.
But we could even make it look better by extracting to method:
const checkString = (value) => {
if (typeof value === "string") {
if (moment(value).isValid()) {
// Do something
} else {
// Do something else
}
}
}
const checkNumber = (value) => {
if (value > 1000000 || value < -1000000) {
//Do something
} else {
//Do something else
}
}
Polymorphism
Besides simply extracting to methods, we can also replace a conditional with polymorphism. By using subclasses, we can create “branches” with the relevant method call.
public class BaseService
{
public bool Authenticate()
{
switch (type)
{
case AUTHENTICATED:
return true;
case UNAUTHORIZED:
return false;
default:
throw new Exception("Server Error!")
}
}
}
Turn LOGGEDIN and UNAUTHORIZED into classes
public abstract class BaseService
{
public abstract bool Authenticate();
}
public class AuthenticatedService : BaseService
{
public override bool Authenticate()
{
return true;
}
}
public class UnauthorizedService : BaseService
{
public override bool Authenticate()
{
return false;
}
}
var auth = service.Authenticate();
Replace Parameter With Explicit Methods
Sometimes polymorphism can be overkill and it may make sense to add setters.
void SetUserAdminName(string userName, bool isAdmin)
{
if(isAdmin)
{
var name = userName;
}
if(userName == null)
{
var name = "";
}
}
Instead just create setters and extract methods into own methods.
void setAdmin(bool isAdmin)
{
_isAdmin = isAdmin
}
void setUsername(string userName)
{
_userName = userName;
}
Introduce Null Object
In OOP, we’re not allowed to use a null reference, so we write null checks. The goal of Null Object Pattern is to minimize if/else chains.
public interface INetwork
{
public string GetAddress();
public bool IsTCP();
]
public class Network : INetwork
{
public string GetAddress() => "8.8.8.8";
public bool IsTCP(); => true;
}
Null Object Pattern is easily identifiable by classes with Null followed by entity (ex: “NullNetwork”)
public class NullNetwork: INetwork
{
public string GetAddress() => "0.0.0.0";
public bool IsTCP() => true;
}
This helps reduce code in our if/else statement by having a default NullNetwork class we can return if nothing works.
public class NetworkFactory {
public static INetwork GetNetwork(string type)
{
if(type != null)
//code here
else
return new NullNetwork();
}
}