The SOLID principles are a set of five principles that help in designing software that's easy to maintain, extend, and scale. Let’s break down each principle with a simple PHP example to illustrate.
---
1. **Single Responsibility Principle (SRP)**
**Definition**: A class should have only one reason to change, meaning it should only have one job or responsibility.
**Example**: Let’s say we have a class `Invoice` that handles creating an invoice and sending it via email. This would violate SRP, as it has two responsibilities (creating and sending). Let’s split it into two classes.
```php
// Violating SRP
class Invoice {
public function createInvoice() {
// Code to create invoice
}
public function sendEmail() {
// Code to send invoice email
}
}
```
**Solution (Following SRP)**:
```php
class Invoice {
public function createInvoice() {
// Code to create invoice
}
}
class EmailSender {
public function sendEmail() {
// Code to send email
}
}
```
Now, each class has a single responsibility.
---
2. **Open/Closed Principle (OCP)**
**Definition**: Classes should be open for extension but closed for modification. You should be able to add new functionality without changing existing code.
**Example**: Let’s say we have a `PaymentProcessor` class that calculates discounts. To add new discount types, we can use inheritance or interfaces instead of modifying the existing class.
```php
// Violating OCP
class PaymentProcessor {
public function calculateDiscount($type) {
if ($type === 'percentage') {
// Calculate percentage discount
} elseif ($type === 'fixed') {
// Calculate fixed discount
}
}
}
```
**Solution (Following OCP)**:
```php
interface Discount {
public function calculate($amount);
}
class PercentageDiscount implements Discount {
public function calculate($amount) {
return $amount * 0.10; // 10% discount
}
}
class FixedDiscount implements Discount {
public function calculate($amount) {
return $amount - 5; // $5 discount
}
}
```
Now, we can add new discount types without modifying `PaymentProcessor`.
---
3. **Liskov Substitution Principle (LSP)**
**Definition**: Subclasses should be substitutable for their base classes without altering the correctness of the program.
**Example**: If we have a `Rectangle` class and create a `Square` subclass, it can violate LSP if `Square` behaves differently from `Rectangle`.
```php
class Rectangle {
protected $width;
protected $height;
public function setWidth($width) {
$this->width = $width;
}
public function setHeight($height) {
$this->height = $height;
}
public function getArea() {
return $this->width * $this->height;
}
}
class Square extends Rectangle {
public function setWidth($width) {
$this->width = $width;
$this->height = $width; // Problematic for LSP
}
public function setHeight($height) {
$this->width = $height;
$this->height = $height; // Problematic for LSP
}
}
```
**Solution**: Avoid this by not making `Square` a subclass of `Rectangle`.
---
### 4. **Interface Segregation Principle (ISP)**
**Definition**: Clients should not be forced to depend on interfaces they do not use. It’s better to have more specific interfaces than a single general-purpose interface.
**Example**: Imagine an interface that forces classes to implement methods they don’t need.
```php
// Violating ISP
interface Worker {
public function work();
public function sleep();
}
class HumanWorker implements Worker {
public function work() {
// Work code
}
public function sleep() {
// Sleep code
}
}
class RobotWorker implements Worker {
public function work() {
// Work code
}
public function sleep() {
// Robots don't sleep - violates ISP
}
}
```
**Solution (Following ISP)**:
```php
interface Workable {
public function work();
}
interface Sleepable {
public function sleep();
}
class HumanWorker implements Workable, Sleepable {
public function work() {
// Work code
}
public function sleep() {
// Sleep code
}
}
class RobotWorker implements Workable {
public function work() {
// Work code
}
}
```
Now `RobotWorker` is not forced to implement `sleep()`.
---
5. **Dependency Inversion Principle (DIP)**
**Definition**: High-level modules should not depend on low-level modules. Both should depend on abstractions. Abstractions should not depend on details; details should depend on abstractions.
**Example**: Let’s say we have a `Database` class that a `UserService` class depends on.
```php
// Violating DIP
class Database {
public function connect() {
// Connect to database
}
}
class UserService {
private $db;
public function __construct() {
$this->db = new Database(); // Tight coupling
}
}
```
**Solution (Following DIP)**:
```php
interface DatabaseConnection {
public function connect();
}
class MySQLConnection implements DatabaseConnection {
public function connect() {
// MySQL connection code
}
}
class UserService {
private $db;
public function __construct(DatabaseConnection $db) {
$this->db = $db;
}
}
```
Now `UserService` depends on an interface rather than a specific class, which allows us to use any type of database connection (MySQL, PostgreSQL, etc.).
---
Following these principles can lead to cleaner, more maintainable, and extensible PHP code!
Comments
Post a Comment