Dezign Patterns
Open/Closed Principle (OCP)
📜 Definition : Software entities (classes, modules, functions) should be open for extension, but closed for modification.🎯 Intent : You should be able to add new features without changing existing code.
In the below example we say that the Circle and Square classes are closed for modification, Shape interface is open for extension.
abstract class Shape {
abstract double area();
}
class Circle extends Shape {
double radius;
public double area() { return Math.PI * radius * radius; }
}
class Square extends Shape {
double side;
public double area() { return side * side; }
}
❌ Open Close Principle Violation Example
🧠 General Rule of Thumb:
If your class has a lot of if-else or switch statements or keeps changing as you add features, it's probably violating OCP. 👀
❌ 1. If-Else Chains for Type Checking
class PaymentProcessor {
public void processPayment(String type) {
if (type.equals("CREDIT")) {
// credit card logic
} else if (type.equals("PAYPAL")) {
// PayPal logic
} else if (type.equals("BITCOIN")) {
// Bitcoin logic
}
}
}
public interface PaymentMethod {
void processPayment();
}
public class CreditCardPayment implements PaymentMethod {
@Override
public void processPayment() {
System.out.println("Processing Credit Card Payment...");
// Credit card logic
}
}
public class PayPalPayment implements PaymentMethod {
@Override
public void processPayment() {
System.out.println("Processing PayPal Payment...");
// PayPal logic
}
}
public class BitcoinPayment implements PaymentMethod {
@Override
public void processPayment() {
System.out.println("Processing Bitcoin Payment...");
// Bitcoin logic
}
}
// Strtegy Context
public class PaymentProcessor {
private PaymentMethod paymentMethod;
// Constructor that accepts a PaymentMethod
public PaymentProcessor(PaymentMethod paymentMethod) {
this.paymentMethod = paymentMethod;
}
// Delegate payment processing to the PaymentMethod
public void processPayment() {
paymentMethod.processPayment();
}
}
Client code
public class Main {
public static void main(String[] args) {
// Choose payment method dynamically
PaymentMethod creditCardPayment = new CreditCardPayment();
PaymentMethod paypalPayment = new PayPalPayment();
PaymentMethod bitcoinPayment = new BitcoinPayment();
// Create PaymentProcessor with different payment methods
PaymentProcessor processor = new PaymentProcessor(creditCardPayment);
processor.processPayment(); // Outputs: Processing Credit Card Payment...
processor = new PaymentProcessor(paypalPayment);
processor.processPayment(); // Outputs: Processing PayPal Payment...
processor = new PaymentProcessor(bitcoinPayment);
processor.processPayment(); // Outputs: Processing Bitcoin Payment...
}
}
❌ 2. Switch Statements on Enum or String
enum ShapeType { CIRCLE, RECTANGLE }
class AreaCalculator {
public double calculateArea(ShapeType shapeType, double value) {
switch (shapeType) {
case CIRCLE: return Math.PI * value * value;
case RECTANGLE: return value * value; // "PRETEND both sides are same"
default: return 0;
}
}
}
❌ 3. Hard-Coded Logic in Classes
class ReportService {
public void generateReport(String format) {
if (format.equals("PDF")) {
// generate PDF
} else if (format.equals("EXCEL")) {
// generate Excel
}
}
}
❌ 4. Adding Features with Boolean Flags
class Notification {
boolean isEmail;
boolean isSMS;
public void send(String message) {
if (isEmail) {
// send email
}
if (isSMS) {
// send SMS
}
}
}
❌ 5. Logging Systems with Manual Control
class Logger {
public void log(String level, String message) {
if (level.equals("INFO")) {
// log info
} else if (level.equals("ERROR")) {
// log error
}
}
}