Principios SOLID

principios SOLID

Estos principios SOLID, nos ayudan a desarrollar un software más mantenible, más claro, más limpio, más escalable, y menos propenso a errores. Fueron definidos por el famoso analista y programador Robert C. Martin, para intentar que los programas fuesen más claros y fáciles de mantener.

¿ Que son los principios SOLID ?

Consta de 5 elementos, y con cada inicial en inglés de estos principios, se forma la palabra SOLID.

  • Principio de responsabilidad única (Single responsability principle) . Dice que cada módulo de software debe tener una única razon para cambiar
  • Principio de abierto/cerrado (Open/Closed principle). Dice que deberías ser capaz de extender el comportamiento de una clase, sin modificarla. En otras palabras, las clases que usas deberían estar abiertas para poder extenderse y cerradas para modificarse.
  • Principio de sustitucion de Liskov (Liskov substitution principle). Las clases derivadas deben poder sustituirse por sus clases base.
  • Principio de segregacion de la interfaz(Interface segregation principle). Haz interfaces que sean para un tipo de cliente
  • Principio de inversion de dependencias(Dependency inversion principle). Depende de abstracciones, no de clases concretas.Aqui tambien se puede decir que :
    • Los módulos de alto nivel no deberían depender de módulos de bajo nivel. Ambos deberían depender de abstracciones.
    • Las abstracciones no deberían depender de los detalles. Los detalles deberían depender de las abstracciones.

Ahora veremos un poco más en detalle cada uno de ellos.

Principio de responsabilidad única

Una clase o función, en definitiva una entidad de código, debería tener sólo una única responsabilidad. En el momento en que realiza más de una tarea, esa clase o función ya está acoplada. Esto provoca que un cambio en una responsabilidad, puede afectar a la otra o viceversa. Este principio, es uno de los más importantes, y de los primeros que viene mencionado en el gran libro «Código Limpio» de Robert C. Martin

Por ejemplo, veamos una clase :

class Vehiculo {
    private $identificador;
    public function __construct(string $identificador){
        $this->identificador = $identificador;
    }
    public saveVehiculo(Vehiculo $vehiculo){
    ...
    }
    public function getVehiculo(): Vehiculo{
    ...
    }
    public function getIdentificadorVehiculo(): int {
    ...
    }
}

Esta clase lo incumple, ya que hace tareas de obtener información, y de leer y almacenar en base de datos.

Una forma de solucionar el problema sería :

class Vehiculo
{
    private $tipo;
    public function __construct(string $tipo){
        $this->tipo = $tipo;
    }
    public function getTipoVehiculo() {
    }
}
class VehiculoDB {
     public function saveVehiculo(Vehiculo $vehiculo) { } 
     public function getVehiculo(String $identificador): Vehiculo { 
    }
}

Principio de abierto/cerrado

El segundo principio está todavía en nuestra clase Vehiculo. Supongamos que tenemos que recorrer una lista de vehiculos, para imprimir el número de ruedas.

$vehiculos = [new Vehiculo('coche'), new Vehiculo('camion'), new Vehiculo('bici')];

foreach($vehiculos as $vehiculo) {
    if ($vehiculo->getTipo() === 'coche') {
        $cont += 4;
    }
    if ($vehiculo->getTipo() === 'bici' {
        $cont += 2;
    }
    if ($vehiculo->getTipo() === 'camion') {
        $cont += 6;
    }
}

Si queremos añadir un nuevo tipo de vehiculo, tendriamos que añadir la lógica a los ifs para el nuevo.

Esto se resuelve con herencia, extendiendo cada clase de Vehiculo, e implementando en cada clase concreta la funcion para saber cuantas ruedas tiene.

class Vehiculo
{
}
class Coche extends Vehiculo ()
{
    public function obtenerRuedas(): int {
        return 4;
    }
}

class Camion extends Vehiculo()
{
    public function obtenerRuedas(): int {
        return 6;
    }
}

class Bici extends Vehiculo()
{
    public function obtenerRuedas(): int {
        return 2;
    }
}

Principio de sustitucion de Liskov

Para el principio de sustitución de Liskov, el objetivo es que se pueda sustituir una clase por su clase padre sin que nada falle.

En el ejemplo de la funcion para calcular las ruedas de los vehiculos, hay que conocer el tipo del vehiculo para llamar al método.

Entonces, para solventar este problema, la funcion quedaria así :

function calcularRuedas(array $vehiculos) {
    foreach($vehiculos as $vehiculo) {
        $cont += $vehiculo->obtenerRuedas();
    }
}

Principio de segregacion de la interfaz

El principio de segregación de interfaz nos dice que no añadamos funcionalidad a las interfaces que no vayamos a usar. Es mejor crear una interfaz separada con poca funcionalidad para ser usada solo desde las clases que las vayan a usar.

Por ejemplo esta interfaz :

Interface ServicioVehiculo
{
    public funciton repararRueda();
    public function repararLuz();
    public function reparaAirbag();
}

Para poder cumplir con este principio, podriamos rehacer la interfaz como sigue :

Interface RuedasService()
{
    public function repararRueda();
}
Interface LucesService()
{
    public function repararLuz();
}
Interface AirbagService()
{
    public function repararAirbag();
}

El el último principio de inversion de dependencia nos dice que las clases de alto nivel no deben depender de las de bajo nivel y también nos dice que las abstracciones no deben depender de los detalles.

Por ejemplo, si tenemos un servicio para obtener vehiculos desde una API, y tenemos :

class VehiculoService()
{
    ...
    public function __construct(){
        $this->HTTPClient = new HTTPClient();
        $this->databaseClient = new Database();
        ...
    }
}

Primero tenemos que abstraer con interfaces, en vez de depender de clases.

Interface HTTPServiceInterface {
    public function get();
    public function post();
}
class HttpService implements HttpServiceInterface { 

}

De este modo, dependemos de la interfaz, no de la implementación.

Principio de inversion de dependencias

Y para resolver el problema de depender de la clase dentro, le pasaremos a la clase las dependencias a través del constructor de esta forma :

class VehiculoService 
{
    public function __construct(HTTPClientInterface $httpClient, DabataseInterface $database)
    {
        $this->HTTPClient = $httpClient;
        $this->databaseClient = $database;
    }
}

De este modo estaríamos cumpliendo con todos los principios solid en nuestro sistema de gestion de vehiculos.

Conclusión sobre principios SOLID

No son suficientes estos 5 principios, pero es un paso importante y un punto por donde comenzar para escribir un código de buena calidad.

Es una forma directa, concisa y clara de empezar a organizar mejor el código para que perdure en el tiempo, y el mantenimiento de este se haga de una forma más rápida y segura. Para este cometido, se podria incluir en el apartado de seguridad, implementar técnicas de TDD (Test Driven Design) o diseño guiado por pruebas, y así poder modificar el código y comprobar que funciona igual que antes.

Resumen
Los 5 principios SOLID explicados con ejemplo
Nombre del artículo
Los 5 principios SOLID explicados con ejemplo
Descripción
Estos principios SOLID, nos ayudan a desarrollar un software más mantenible, más claro, más limpio, más escalable, y menos propenso a errores. Fueron definidos por el famoso analista y programador Robert C. Martin, para intentar que los programas fuesen más claros y fáciles de mantener.
Autor
Publisher Name
www.joseantoniocuenca.com
Publisher Logo

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Consentimiento de Cookies de acuerdo al RGPD con Real Cookie Banner