You've learned all about OOP in PHP, but now what? When you make an object, you have no idea what to do with it. This is why Design Patterns matter.
I have been working as developer and tech lead for 19 years. I’ve written code for over 200 projects. Some of it was cool, most of it was really boring. The cooler the code, the harder it is to maintain. The best code is simple and easy to explain, and the way it works even has a name.
The name that your senior colleagues will recognize. It's name is one of the design patterns, like these:
Problem: I need to use one class inside another, and my code’s becomes a mess quick
Solution: Dependency Injection
by injecting dependencies, you pass configured objects into a class - instead of creating them inside another class (thus developing dependency)
class UserController {
private $userService;
public function __construct(UserService $userService) {
$this->userService = $userService;
}
public function getUser($id) {
return $this->userService->findUser($id);
}
}
// Usage
$controller = new UserController(new UserService());
Problem: I need to create complex objects, but it’s a lot of copy-paste. That can't be good, right?
Solution: Factory Pattern
Instead of creating objects everywhere, let a Factory handle it.
class UserFactory {
public function create($userId, $userType) {
switch ($userType) {
case 'admin':
return new AdminUser($userId);
case 'visitor':
return new VisitorUser($userId);
default:
throw new Exception("Invalid user type");
}
}
}
// Usage example
$userFactory = new UserFactory();
$adminUser = $userFactory->create(1, 'admin');
$visitorUser = $userFactory->create(2, 'visitor');
interface Product {
public function getPrice();
public function getDescription();
}
class BasicProduct implements Product {
public function getPrice() { return 100; }
public function getDescription() { return "Basic product"; }
}
class DiscountDecorator implements Product {
public function __construct(private Product $product) {}
public function getPrice() { return $this->product->getPrice() - 20; }
public function getDescription() { return $this->product->getDescription() . ", with discount"; }
}
class GiftWrapDecorator implements Product {
public function __construct(private Product $product) {}
public function getPrice() { return $this->product->getPrice() + 5; }
public function getDescription() { return $this->product->getDescription() . ", gift wrapped"; }
}
// Usage
$product = new GiftWrapDecorator(new DiscountDecorator(new BasicProduct()));
echo $product->getDescription() . " costs $" . $product->getPrice();
class OrderProcessor {
public function processOrder($orderId) {
$inventory = new Inventory();
$payment = new Payment();
$shipping = new Shipping();
if ($inventory->checkStock($orderId) && $payment->processPayment($orderId)) {
$shipping->shipOrder($orderId);
return "Order processed successfully.";
}
return "Order processing failed.";
}
}
class Inventory {
public function checkStock($orderId) { return true; }
}
interface Observer {
public function update($message);
}
class ConcreteObserver implements Observer {
public function __construct(private $name) {}
public function update($message) {
echo "$this->name received: $message\n";
}
}
class Subject {
private $observers = [];
public function attach(Observer $observer) { $this->observers[] = $observer; }
public function notify($message) {
foreach ($this->observers as $observer) $observer->update($message);
}
}
// Usage
$subject = new Subject();
$subject->attach(new ConcreteObserver("Observer 1"));
$subject->attach(new ConcreteObserver("Observer 2"));
$subject->notify("Hello Observers!");
I've made few videos on how to apply these patterns on real life code
First one is a real life code a student has sent me for review
and second one is a sneap peek inside internals of popular framework like Laravel
Categories: : Clean code