Why Your Code Is Overrun by if‑else and How to Refactor It Effectively
The article explains why developers often end up with excessive if‑else statements, outlines the drawbacks of deep nesting, and presents practical refactoring techniques—including condition merging, early exits, and polymorphism—to produce cleaner, more maintainable code.
Why do we write code with if‑else?
Developers often start with simple, clear code, but as requirements evolve they add numerous type and value checks, null checks, and different process branches, leading to a proliferation of if‑else statements, bloated functions, and files that become hard to maintain.
Although we dislike writing screens of if‑else, such logic is sometimes unavoidable for special cases.
In practice, if‑else appears mainly in two scenarios: exception handling and state handling.
The key difference is that exception handling has one normal flow and the rest are error branches, whereas state handling treats all branches as normal flows.
Drawbacks of excessive if‑else
Too many if‑else statements make code logic complex, hard to maintain, and prone to bugs. Treating all branches equally can cause misunderstandings because most branches are not equally important.
How to optimize and refactor
When refactoring if‑else, keep the normal flow at the outermost level and avoid deep nesting. Techniques include reducing nesting, removing temporary variables, inverting conditions, and merging condition expressions.
Exception‑handling refactoring example 1
Before:
double disablityAmount() {
if(_seniority < 2)
return 0;
if(_monthsDisabled > 12)
return 0;
if(_isPartTime)
return 0;
// do something
}After:
double disablityAmount() {
if(_seniority < 2 || _monthsDisabled > 12 || _isPartTime)
return 0;
// do something
}This merges multiple conditions into a single expression, reducing the number of if statements.
Exception‑handling refactoring example 2
Before:
double getPayAmount() {
double result;
if(_isDead) {
result = deadAmount();
} else {
if(_isSeparated) {
result = separatedAmount();
} else {
if(_isRetired) {
result = retiredAmount();
} else {
result = normalPayAmount();
}
}
}
return result;
}After:
double getPayAmount() {
if(_isDead)
return deadAmount();
if(_isSeparated)
return separatedAmount();
if(_isRetired)
return retiredAmount();
return normalPayAmount();
}The refactored version returns early on error conditions, keeping the main flow clear.
Exception‑handling refactoring example 3
Before:
public double getAdjustedCapital() {
double result = 0.0;
if(_capital > 0.0) {
if(_intRate > 0 && _duration > 0) {
result = (_income / _duration) * ADJ_FACTOR;
}
}
return result;
}After applying early exits and condition inversion:
public double getAdjustedCapital() {
if(_capital <= 0.0)
return 0.0;
if(_intRate <= 0 || _duration <= 0)
return 0.0;
return (_income / _duration) * ADJ_FACTOR;
}State‑handling refactoring example 1
Before:
double getPayAmount() {
Object obj = getObj();
double money = 0;
if(obj.getType == 1) {
ObjectA objA = obj.getObjectA();
money = objA.getMoney() * obj.getNormalMoneryA();
} else if(obj.getType == 2) {
ObjectB objB = obj.getObjectB();
money = objB.getMoney() * obj.getNormalMoneryB() + 1000;
}
return money;
}After extracting methods:
double getPayAmount() {
Object obj = getObj();
if(obj.getType == 1) {
return getType1Money(obj);
} else if(obj.getType == 2) {
return getType2Money(obj);
}
return 0;
}
double getType1Money(Object obj) {
ObjectA objA = obj.getObjectA();
return objA.getMoney() * obj.getNormalMoneryA();
}
double getType2Money(Object obj) {
ObjectB objB = obj.getObjectB();
return objB.getMoney() * obj.getNormalMoneryB() + 1000;
}State‑handling refactoring example 2 (polymorphism)
Original switch‑case:
double getSpeed() {
switch(_type) {
case EUROPEAN: return getBaseSpeed();
case AFRICAN: return getBaseSpeed() - getLoadFactor() * _numberOfCoconuts;
case NORWEGIAN_BLUE: return (_isNailed) ? 0 : getBaseSpeed(_voltage);
}
}Refactored using polymorphism:
abstract class Bird {
abstract double getSpeed();
}
class European extends Bird {
double getSpeed() { return getBaseSpeed(); }
}
class African extends Bird {
double getSpeed() { return getBaseSpeed() - getLoadFactor() * _numberOfCoconuts; }
}
class NorwegianBlue extends Bird {
double getSpeed() { return (_isNailed) ? 0 : getBaseSpeed(_voltage); }
}Summary
If‑else code is easy to write but easy to abuse, leading to hard‑to‑maintain logic. The main refactoring principle is to keep the normal flow at the outermost level, merging conditions where possible, reducing nesting, and exiting early on error cases. For state handling, either extract each branch into a dedicated method or use polymorphism to eliminate conditional logic altogether.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
