Cyb3rh4ck / Implementando los Principios SOLID (y Reglas de Oro)

Created Tue, 31 Mar 2026 12:55:00 -0600

La diferencia entre un código que amas modificar y uno que te da terror abrir reside en 5 simples letras descubiertas hace dos décadas.

🚀 Implementando los Principios SOLID

Los principios SOLID constituyen una colección de cinco guías o reglas de diseño cuyo objetivo es mejorar la comprensión, flexibilidad y total mantenibilidad de los diseños de software.

Fundados dentro del marco del Diseño Orientado a Objetos (OOD), estos principios fueron dados a conocer por Robert C. Martin (a menudo referido como Uncle Bob) a principios de la década de los 2000. Desde entonces, han ganado una inmensa aceptación dentro de la toda comunidad de la Ingeniería de Software como estrategias esenciales y de oro para estructurar cualquier sistema de código.

SOLID es un acrónimo donde cada letra representa lo siguiente:

  • Single Responsibility Principle
  • Open/Closed Principle
  • Liskov Substitution Principle
  • Interface Segregation Principle
  • Dependency Inversion Principle

Veamos cada uno de ellos llevado a la práctica.


S - Single Responsibility Principle (SRP)

El Principio de Responsabilidad Única (SRP) sugiere que una clase solo debe tener una sola responsabilidad o propósito, lo que significa que solo debe existir un único motivo por el cual deba ser modificada a lo largo del tiempo. Apegarte a esto vuelve tu código extremadamente modular, permitiendo que realices cambios de código en una parte integral del sistema y no afectes o tumbes ninguna otra funcionalidad de forma inadvertida. Esto a su vez asegura una alta cohesión, volviendo la clase más fácil de mantener, entender y por supuesto probar (testing).

Implementando SRP: Imaginemos una biblioteca para ejemplificar el SRP:

public class Book {
    private String title;
    private String author;
    
    public Book(String title, String author) { ... }
}

public class BookPersistence {
    public void save(Book book) { ... }
}

Aquí la clase Book está enfocada enteramente a representar las propiedades y el comportamiento natural de un libro. Por el otro lado, introducimos BookPersistence, la cual es responsable dedicada de guardar dicho libro a una base. Esto separa formalmente la preocupación o concern de la capa de persistencia lejos de la clase base Book, logrando así una arquitectura SRP ideal.


O - Open/Closed Principle (OCP)

El Principio Abierto y Cerrado (OCP) dicta que los diferentes módulos o clases deben estar siempre abiertos para su extensión pero firmemente cerrados para su alteración; lo que realmente significa es que tu aplicación debe ser capaz de absorber integraciones y funcionalidades nuevas sin tocar o destruir los bloques de la lógica ya construidos previamente.

Implementando OCP: Tomando el ejemplo de los libros anterior, demostremos cómo utilizar OCP para ser capaces de guardar libros en diferentes fuentes de datos ¡sin tener que tocar la clase original BookPersistence!:

Lo lograremos introduciendo una abstracción del guardado del libro a través de una Interfaz (BookSaveFormat), que eventualmente nutrirá diferentes implementaciones (como para PDFs, texto plano y bases de datos completas).

public interface BookSaveFormat {
    void save(Book book);
}

Luego, las distintas implementaciones de texto y base de datos nacen a partir de ella, inyectando su lógica técnica separada:

public class TextFileSaveFormat implements BookSaveFormat {
    public void save(Book book) {
        System.out.println("Guardando el libro '" + book.getTitle() + "' a un archivo plano de File System.");
    }
}

public class DbSaveFormat implements BookSaveFormat {
    public void save(Book book) {
        System.out.println("Insertando el libro '" + book.getTitle() + "' a DB Oracle.");
    }
}

Al final del pipeline, la propia la clase creadora original, la BookPersistence, es rediseñada para aceptar cualquier instancia genérica de BookSaveFormat a través de su constructor:

public class BookPersistence {
    private BookSaveFormat saveFormat;
    
    public BookPersistence(BookSaveFormat saveFormat) {
        this.saveFormat = saveFormat;
    }
    
    public void saveBook(Book book) {
        this.saveFormat.save(book);
    }
}

Con este modelo OCP, logramos dejar nuestro motor permanentemente abierto a nuevas extensiones (como añadir formato de salida .json en un futuro sin mucho esmero), pero 100% blindado y cerrado para cualquier alteración, ya que la vieja clase BookPersistence no será quebrantada por agregar nuevos formatos a nivel base.


L - Liskov Substitution Principle (LSP)

El Principio de Sustitución de Liskov (LSP) es una genialidad semántica: afirma que los objetos construidos dentro de una superclase siempre deberían ser reemplazables fácilmente por sus objetos o clones de su respectiva subclase, esto sin destruir jamás la operatividad o correctitud general del programa o proyecto central.

Implementando LSP: Retomemos los libros. Vamos a dar a luz a una clase EBook (Libro Electrónico) que logre extender la rama de nuestro original genérico Book:

public class EBook extends Book {
    private String url;
    // Getters y Setters...
}

Ahora en una aplicación formal, asumiremos la existencia de un displayBookDetails(). Idealmente, un sistema LSP aceptará cualquier clase Book, pero a su vez logrará inyectar sin errores los detalles especiales inyectados por la subclase de libro electrónico si el molde recibido así lo presenta:

public class Application {
    public static void displayBookDetails(Book book) {
        System.out.println(book.getTitle());
        
        if (book instanceof EBook) {
            System.out.println(((EBook)book).getDownloadUrl());
        }
    }
}

[!NOTE] El chequeo usando la palabra reservada de Java instanceof es meramente ilustrativo para esta sección académica, debido a que en el mundo real u orientado a dominios modernos, dichas validaciones con if son reemplazadas a fondo aprovechando la ejecución polimórfica (y sobreescrituras explícitas nativas a través del árbol de clases).


I - Interface Segregation Principle (ISP)

El Principio de Segregación de Interfaz (ISP) dictamina e implora que a los clientes lógicos nunca deberían obligárseles a depender de interfaces monstruosas si en realidad solo harán uso e invocación del 30% de sus métodos. ¿Sugerencia principal? Trocear las grandes interfaces y volverlas mucho más específicas y chicas.

Implementando ISP: Observa este pequeño desastre inicial de Interfaz, concebida idealmente a futuro para las clases Revistas (Magazine, donde ocupan el código de rastreo un ISSN) o los Libros (Book, los cuales implementan formalmente código rastreador ISBN):

public interface Publication {
    void displayInfo();
    void validateIsbn(String isbn); // ❌ Un método molesto para una Revista
    void validateIssn(String issn); // ❌ Un método molesto para un Libro
}

El enfoque sano de ISP solucionaría los disgustos del autor desintegrando a todo esto en bloques más naturales e independientes para sus consumidores finales:

public interface ValidateIsbn {
    void validate(String isbn);
}

public interface ValidateIssn {
    void validate(String issn);
}

public interface Publication {
    void displayInfo();
}

Así un Book solamente optaría por usar aquellos rasgos genéticos a nivel software que en serio necesite proveer en el código terminal:

public class Book implements ValidateIsbn, Publication {
    void displayInfo() { ... }
    void validateIsbn(String isbn) { ... }
}

El libro no tiene que mentir o generar un método vacío falso para cubrir al forzado validateIssn(), cumpliéndose al fin nuestra premisa central de ISP.


D - Dependency Inversion Principle (DIP)

Finalmente el Principio de Inversión de Dependencias (DIP) alerta abiertamente que los módulos en los eslabones superiores de prioridad (Alto Nivel) deberían evitar rotundamente vincularse con la lógica dependiente de los módulos en las jerarquías bajas (Bajo Nivel). Para mitigar este roce vertical de acoplamiento, todo debe estar construido partiendo de las abstracciones. Un detalle no debe gobernar la abstracción; las abstracciones siempre dictan los detalles.

Implementando DIP: Llevando esto a DIP formal, nuestra biblioteca generará unos eslabones intermedios o proxys (Interfaces BookPersistence y BookSaveFormat) que lograrán actuar de punto neutral, aislando enteramente a los módulos dependientes entre capas altas lógicas y capas transaccionales:

public interface BookPersistence {
    void save(Book book);
}

public interface BookSaveFormat {
    void save(Book book);
}

La estructura final BookPersistenceImpl podrá implementar a su antepasado genérico absteniéndose religiosamente de interactuar a bajo nivel con tecnologías o clases externas dependientes, manteniendo su enfoque total usando únicamente la capa superior BookSaveFormat libremente en vez de sus formas dependientes tangenciales.

public class BookPersistenceImpl implements BookPersistence {
    private BookSaveFormat saveFormat; 
    
    public BookPersistenceImpl(BookSaveFormat saveFormat){
        this.saveFormat = saveFormat;
    }
    
    public void saveBook(Book book) {
        saveFormat.save(book);
    }
}

En tiempo real, en la clase o inyector cliente, bastará con mandar al campo de batalla la pieza preconfigurada exacta:

Book book = new Book("Patrones limpios", "Juan Pérez");
BookPersistence bookPersistence = null;

if (saveType.equals("T")) {
    bookPersistence = new BookPersistenceImpl(new TextFileSaveFormat());
} else {
    bookPersistence = new BookPersistenceImpl(new DbSaveFormat());
}

bookPersistence.saveBook(book); // Magia pura abstrayendo detalles externos

Para entender estos conceptos verdaderamente a su mayor profundidad e impacto financiero/lógico en desarrollos, recomiendo personalmente la lectura del libro oficial Clean Architecture: A Craftsman’s Guide to Software Structure and Design del propio Robert C. Martin.


🛠️ Más Allá de SOLID (KISS, DRY, YAGNI y Demeter)

Las siglas SOLID no tienen el monopolio de las buenas prácticas. Toda gran base de datos de conocimientos tecnológicos incluye por omisión herramientas para mitigar los sesgos más comunes de nosotros como Arquitectos.

Te presento otras letras importantísimas de memorizar en tu día a día:

1. Keep It Simple, Stupid (KISS)

El principio (KISS, del inglés “Mantenlo simple, estúpido”) hace resaltar la abrumadora importancia de la pura simplicidad estructural tras cada fase de diseño e implementación del software moderno. Motiva encarecidamente la idea general de que sistemas más legibles y pequeños son los sistemas más reparables, fuertes y afortunados del mercado de soporte. Simplemente te indica: “aléjate de los enriedos a menos que te pidan explícitamente armar el cubo de rubik”.

2. Don’t Repeat Yourself (DRY)

Este es probablemente uno de los dichos más aclamados a diario en equipos junior, “DRY” (No te repitas) expone y aborda el peligro de tener piezas y rutinas clonadas masivamente archivo tras archivo mediante el legendario “copiar y pegar”. Visto inicialmente a gran escala a principios de milenio tras las escrituras en el libro The Pragmatic Programmer de Hunt & Thomas, DRY te exigirá crear variables, beans o un punto de concentración unitario e indiscutible (único) donde la abstracción descanse a gusto para ser reutilizada mil y un veces sin repetir bloques textuales tontamente a costa del espacio en memoria de disco.

3. You Aren’t Gonna Need It (YAGNI)

YAGNI significa “No lo vas a necesitar”. Es un principio de desarrollo de software que desaconseja por completo agregar funcionalidades o capas de código adicionales hasta que éstas sean estricta y verdaderamente necesarias.

Este enfoque enfatiza primordialmente la simplicidad y la eficiencia, instando fuertemente a los desarrolladores a concentrarse de forma exclusiva en los requisitos medulares e inmediatos del sistema, en lugar de perder recursos, dinero y tiempo en especulaciones futuras diseñando código “por si acaso llegamos a necesitarlo algún día”.

El principio nos recuerda priorizar siempre la entrega de valor urgente y evitar inyectar complejidad innecesaria u oscurecer la arquitectura hasta que se demuestre, con requerimientos de negocio concretos, que el añadido es obligatorio. Seguir fielmente la filosofía YAGNI siempre conducirá a procesos de desarrollo mucho más eficientes, entregas de producto más veloces (Time to Market) y, en última instancia, una base de código infinitamente más fácil de mantener y hacer evolucionar de cara al usuario.

4. La Ley de Demeter (The Law of Demeter)

La Ley de Demeter (LoD), también conocida informalmente en el gremio como el principio del menor conocimiento (Principle of Least Knowledge), sirve como una estricta recomendación de diseño en la ingeniería de software que promueve reducir el temido grado de acoplamiento entre distintos módulos y clases.

Originada en la Universidad del Nordeste a finales de la década de 1980, esta elegante ley empírica sugiere que un método particular de un objeto solo y únicamente debería invocar y llamar a interactuar a aquellos métodos que provengan de las siguientes fuentes:

  • Del objeto en sí mismo.
  • De los objetos pasados explícitamente como argumentos de la función.
  • De los objetos que él mismo instancie y cree desde cero operativamente.
  • De sus componentes o variables directas.

Este riguroso principio anima firmemente a que los objetos de tu código interactúen mayoritariamente con sus asociados más estrechos y cercanos para disminuir drásticamente sus dependencias base, mejorando radicalmente la encapsulación global. Por otro lado, como “no todo lo que brilla es oro”, aunque su uso sistemático estimule la modularidad y evite un altísimo grado de acoplamiento nocivo (como el clásico e interminable encadenamiento de funciones objeto.uno().dos().tres()), aplicarla ciegamente y de forma excesiva suele resultar en diseños pesados o sumamente complicados debido a la infinita aparición de métodos suplementarios de envoltura (wrapper methods) con el único fin de encubrir el paso de mensajes.


Ahora que ya estamos completamente familiarizados con los profundos principios de la arquitectura del software a nivel lógico y estructural, ¡es hora de prepararnos para obtener una vista panorámica sólida de los estilos arquitectónicos generales más comunes y valorados en la industria actual IT!