Hey there, fellow coder! 👋 Today, we’re going to explore the Open-Closed Principle (OCP), one of the SOLID principles that help you write clean, maintainable, and scalable code.
Don’t worry—we’ll keep it super simple, with real-world examples and minimal Laravel/PHP code. Let’s dive in! 🏊♂️
What is the Open-Closed Principle? 🤔
The Open-Closed Principle says:
“Your code should be open for extension but closed for modification.”
What does that mean?
- Open for extension → You can add new features without breaking things.
- Closed for modification → You don’t change existing code to add those features.
Think of it like a smartphone 📱:
- You can install new apps (extend functionality).
- But you don’t rewrite the phone’s OS (no modification).
Bad Code Example: Payment Processor 💳 (Before OCP)
Let’s say you have a PaymentController
in Laravel that handles two payment methods:
class PaymentController {
public function pay($paymentType, $amount) {
if ($paymentType == 'credit_card') {
// Process credit card payment
return "Charged $amount via Credit Card";
} elseif ($paymentType == 'paypal') {
// Process PayPal payment
return "Charged $amount via PayPal";
}
throw new Exception("Payment method not supported.");
}
}
The Problem 😬
- What if you want to add Stripe, Bank Transfer, or Crypto?
- You keep modifying the same
pay()
method, adding moreif-else
blocks. - This violates OCP because you’re changing existing code instead of extending it.
The OCP-Friendly Solution 🎉
Instead of modifying the PaymentController
, we’ll extend it! Here’s how:
Step 1: Define a Payment Interface (Contract)
interface PaymentMethod {
public function pay($amount);
}
Step 2: Create Separate Payment Classes
class CreditCardPayment implements PaymentMethod {
public function pay($amount) {
return "Charged $amount via Credit Card";
}
}
class PayPalPayment implements PaymentMethod {
public function pay($amount) {
return "Charged $amount via PayPal";
}
}
// Later, you can add more without touching old code!
class StripePayment implements PaymentMethod {
public function pay($amount) {
return "Charged $amount via Stripe";
}
}
Step 3: Refactor the PaymentController
class PaymentController {
public function pay(PaymentMethod $paymentMethod, $amount) {
return $paymentMethod->pay($amount);
}
}
Now, Using It is Super Clean!
$paymentController = new PaymentController();
// Credit Card Payment
echo $paymentController->pay(new CreditCardPayment(), 100);
// PayPal Payment
echo $paymentController->pay(new PayPalPayment(), 200);
// Stripe Payment (added later, no changes to old code!)
echo $paymentController->pay(new StripePayment(), 300);
Why This is Awesome? ✨
✅ No more if-else
spaghetti – Each payment type has its own class.
✅ Easy to add new payments – Just create a new class (BitcoinPayment
, BankTransfer
, etc.).
✅ Zero changes to existing code – Follows OCP perfectly!
Real-World Example: Laravel Notifications 🔔
Laravel itself follows OCP! When you send notifications (email, SMS, Slack), you don’t modify Laravel’s core—you just extend it:
// Define a Notification
class InvoicePaid extends Notification {
public function via($notifiable) {
return ['mail', 'database']; // Extend channels easily!
}
}
Want to add Slack later? Just update via()
—no core changes needed!
Final Thoughts 🧠
The Open-Closed Principle keeps your code flexible and future-proof. Instead of hacking old code, extend it with new classes.
Remember:
🛑 Don’t modify – Avoid changing working code.
✅ Do extend – Add new features via interfaces & classes.
Now go refactor something! 🚀 Happy coding! 💻
Leave a Reply