Los principios SOLID son cinco principios de diseño orientado a objetos que te ayudarán a crear software más robusto, mantenible y escalable. Estos principios fueron popularizados por Robert C. Martin (Uncle Bob) y son fundamentales para el desarrollo de software limpio.
1. S - Single Responsibility Principle (SRP)
Principio de Responsabilidad Única
Cada clase debe tener una única responsabilidad o razón de cambio. En otras palabras, una clase debe hacer una sola cosa y hacerlo bien.
Ejemplo en PHP:
class ReportGenerator {
public function generatePDF($data) {
// Lógica para generar un PDF
}
public function generateCSV($data) {
// Lógica para generar un CSV
}
}
Refactorización:
class PDFReportGenerator {
public function generate($data) {
// Lógica para generar un PDF
}
}
class CSVReportGenerator {
public function generate($data) {
// Lógica para generar un CSV
}
}
Ahora, cada clase tiene una única responsabilidad.
2. O - Open/Closed Principle (OCP)
Principio de Abierto/Cerrado
Las clases deben estar abiertas para la extensión, pero cerradas para la modificación. Esto significa que deberías poder añadir nuevas funcionalidades sin modificar el código existente.
Ejemplo en PHP:
class PaymentProcessor {
public function processCreditCard($amount) {
// Procesar pago con tarjeta de crédito
}
public function processPayPal($amount) {
// Procesar pago con PayPal
}
}
Refactorización:
interface PaymentMethod {
public function pay($amount);
}
class CreditCardPayment implements PaymentMethod {
public function pay($amount) {
// Procesar pago con tarjeta de crédito
}
}
class PayPalPayment implements PaymentMethod {
public function pay($amount) {
// Procesar pago con PayPal
}
}
class PaymentProcessor {
public function process(PaymentMethod $paymentMethod, $amount) {
$paymentMethod->pay($amount);
}
}
Ahora podemos agregar nuevos métodos de pago sin modificar PaymentProcessor
.
3. L - Liskov Substitution Principle (LSP)
Principio de Sustitución de Liskov
Las clases derivadas deben ser sustituibles por sus clases base sin alterar el correcto funcionamiento del programa. En otras palabras, si B
es una subclase de A
, deberías poder reemplazar A
por B
sin problemas.
Ejemplo en 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;
}
public function setHeight($height) {
$this->height = $height;
$this->width = $height;
}
}
En este ejemplo, Square
rompe el principio LSP porque altera el comportamiento esperado de Rectangle
.
4. I - Interface Segregation Principle (ISP)
Principio de Segregación de Interfaces
Las interfaces deben ser específicas y no forzar a las clases a implementar métodos que no necesitan. Es mejor tener varias interfaces pequeñas que una interfaz grande y genérica.
Ejemplo en PHP:
interface Worker {
public function work();
public function eat();
}
class HumanWorker implements Worker {
public function work() {
// Trabajar
}
public function eat() {
// Comer
}
}
class RobotWorker implements Worker {
public function work() {
// Trabajar
}
public function eat() {
// ¡Un robot no necesita comer!
}
}
Refactorización:
interface Workable {
public function work();
}
interface Eatable {
public function eat();
}
class HumanWorker implements Workable, Eatable {
public function work() {
// Trabajar
}
public function eat() {
// Comer
}
}
class RobotWorker implements Workable {
public function work() {
// Trabajar
}
}
De esta forma, las clases solo implementan los métodos que realmente necesitan.
5. D - Dependency Inversion Principle (DIP)
Principio de Inversión de Dependencias
Las clases de alto nivel no deben depender de clases de bajo nivel, sino de abstracciones (interfaces). Las dependencias deben inyectarse en lugar de ser instanciadas directamente.
Ejemplo en PHP:
class Database {
public function connect() {
// Conectar a la base de datos
}
}
class UserRepository {
private $database;
public function __construct() {
$this->database = new Database();
}
public function getUser($id) {
$this->database->connect();
// Obtener usuario de la base de datos
}
}
Refactorización:
interface DatabaseConnection {
public function connect();
}
class MySQLConnection implements DatabaseConnection {
public function connect() {
// Conectar a MySQL
}
}
class UserRepository {
private $database;
public function __construct(DatabaseConnection $database) {
$this->database = $database;
}
public function getUser($id) {
$this->database->connect();
// Obtener usuario de la base de datos
}
}
Ahora, UserRepository
depende de una abstracción (DatabaseConnection
) en lugar de una implementación concreta, lo que facilita el cambio de la base de datos en el futuro.