Why to learn Design Patterns š¤ ?
Introduction to design patterns

āYou donāt learn design patterns because books say so ā
you feel their need when your code becomes a tangled mess.
Letās look at how one extra feature request can turn clean code into chaos ā and how a pattern can save you.ā
š Start with a Real Developerās Struggle
Letās take a very relatable example from my Java or Flutter experience.
Imagine this situation š
š§© Scenario: The āSpaghetti Codeā Factory Problem
Youāre building an eCommerce app.
At first, you only support Card Payments.
So, you write:
if (paymentType.equals("CARD")) {
processCardPayment();
}
All good ā
But later, you add:
UPI Payment
Wallet Payment
Cash on Delivery
Your code becomes:
if (paymentType.equals("CARD")) {
processCardPayment();
} else if (paymentType.equals("UPI")) {
processUPIPayment();
} else if (paymentType.equals("WALLET")) {
processWalletPayment();
} else if (paymentType.equals("COD")) {
processCODPayment();
}
Looks okay?
Wait till the next requirement hits youā¦
āWe need to add Crypto payment next week, and refund feature later.ā
Now every time a new payment method comes, youāll:
Modify this code again š©
Risk breaking something else š©
Have to test everything again š©
This is code that grows horizontally (more if-else) ā it violates the Open/Closed Principle.
Your code is open for modification when it should be open for extension.
š§ And here comes the realizationā¦
You pause and think:
āWhy canāt I just have a flexible system that lets me add new payment methods without touching the core logic?ā
Thatās when you realize you need a Design Pattern.
āļø Solution: Factory Method Pattern
You apply a Factory Method Pattern, which creates payment objects dynamically:
interface Payment {
void process();
}
class CardPayment implements Payment {
public void process() { System.out.println("Processing Card Payment"); }
}
class UPIPayment implements Payment {
public void process() { System.out.println("Processing UPI Payment"); }
}
class PaymentFactory {
public static Payment getPayment(String type) {
return switch (type) {
case "CARD" -> new CardPayment();
case "UPI" -> new UPIPayment();
default -> throw new IllegalArgumentException("Invalid payment type");
};
}
}
Now your main logic becomes:
Payment payment = PaymentFactory.getPayment("CARD");
payment.process();
If you add a new payment method tomorrow (like Crypto),
you create a new class, not change old ones.
ā
Code is extensible
ā
Clean and maintainable
ā
No repeated conditionals
š„ Another Realistic Example: The Singleton Problem
Scenario:
Youāre building a logging system in a backend app.
You notice that multiple parts of your app are writing to logs ā
but different parts create their own logger objects.
You end up with:
Multiple log files,
Duplicate messages,
Messy output.
Then you realize ā
you actually only need one logging instance shared across the entire app.
Thatās when you use a Singleton pattern.



