Principios SOLID de la orientación a objetos

Los requisitos cambian. Es algo con lo que todos tenemos que lidiar cada día. De esto se desprende que, independientemente del tiempo que hayas invertido en la captura de requisitos y de las muchas funcionalidades extra que hayas podido implementar (en contra de lo que dicta YAGNI), tu aplicación va a tener que cambiar. Será mejor que estés preparado ello.

Una forma de estarlo es seguir los 5 principios básicos de la programación orientada a objetos, explicados en “Agile Software Development: Principles, Patterns, and Practices” por uno de los grandes exponentes de la artesanía del software, el famoso “Uncle Bob” (Robert C. Martin)

Estos principios, cuyas primeras letras forman el acrónimo mnemotécnico SOLID (sólido), son los siguientes:

Single Responsibility Principle
(Principio de Responsabilidad Única)

Cada componente, ya sean paquetes, clases, métodos e incluso bloques de código, debería tener una única razón para cambiar. Esto es, debe tener una única responsabilidad, ocuparse de una única tarea, para aumentar de esa forma la cohesión y reducir el acoplamiento.

Supongamos que tenemos una clase Pedido con dos métodos: cargar, que traería de la base de datos la información del pedido y de los productos que lo componen, y calcularCoste, que simplemente sumaría el precio de los productos, aplicando cualquier descuento pertinente.

Esto podría parecer un buen diseño, porque, al fin y acabo, ambos métodos se encargan de cosas relacionadas con los pedidos, pero la realidad es que la clase se encarga de tareas relacionadas con los pedidos y de tareas relacionadas con la base de datos.

Open Closed Principle
(Principio Abierto/Cerrado)

Todo componente debe estar abierto a nuevas funcionalidades, pero cerrado a cambios en su código. Queremos extender el comportamiento del componente sin modificar su código. ¿Cómo hacerlo? Mediante la abstracción, por ejemplo, el polimorfismo.

Supongamos que estoy programando un reproductor de audio, y tengo una clase Archivo con un método reproducir, que consiste en un switch que llama a un método u otro de la clase dependiendo de si el archivo es un MP3, un WMA o un OGG. Si quiero añadir un nuevo formato más tarde, tendré que modificar el switch, añadiendo una nueva expresión que llame a otra función. Si en lugar de utilizar un switch definimos reproducir como abstracto, y movemos la implementación a varias clases hijas, una por cada formato, cuando queramos añadir un nuevo formato, sólo necesitaremos crear una nueva clase hija.

Liskov Substitution Principle
(Principio de Sustitución de Liskov)

Los objetos de una clase deberían poder sustituirse por instancias de las clases derivadas. Llamado así porque fue enunciada por primera vez por Barbara Liskov, premio Turing 2008, puede servirnos para determinar si nuestra jerarquía se ajusta al Principio Abierto/Cerrado.

El ejemplo típico es una clase Cuadrado que hereda de una clase Rectangulo. El ancho y alto de ambas clases se encapsulan mediante métodos get y set, pero en el caso del cuadrado, ambos métodos modifican ambos atributos, de forma que ambas dimensiones coincidan, como es de esperar para un cuadrado. Ahora bien, si el código cliente necesita saber con qué tipo está tratando para poder funcionar y responder adecuadamente, esto viola claramente el Principio Abierto/Cerrado.

Interface Segregation Principle
(Principio de Segregación de Interfaces)

Crea pequeñas interfaces específicas para los clientes. Otra forma de expresarlo es que las clases que implementen una interfaz o una clase abstracta, no deberían estar obligadas a implementar métodos que no utilizan.

Supongamos que tenemos una clase abstracta TelefonoMovil con la que queremos representar tanto móviles actuales como viejas reliquias. No sería una gran idea crear un método fotografiar() en la clase padre, dado que no todos los teléfonos tienen esta funcionalidad. Aunque la implementación del método en la clase concreta consistiera en no hacer nada o lanzar una excepción, este diseño sólo serviría para crear confusión y dificultar el mantenimiento.

Dependency Inversion Principle
(Principio de Inversión de Dependencias)

Depende de abstracciones, no de implementaciones concretas.

Se llama así porque, al contrario de lo que sucede a veces, las clases concretas deberían depender de clases abstractas, y no a la inversa.

Supongamos que tenemos una clase que se encargará de gestionar la configuración de la aplicación, y esta utiliza (tiene una dependencia de) una clase que permite leer y escribir de un archivo de texto plano. Pero, ¿y si de repente decidimos que queremos utilizar una base de datos? ¿o un archivo XML? Lo ideal sería que nuestra clase utilizara una clase abstracta o una interfaz, en lugar de una implementación concreta, como era la clase para trabajar con archivos de texto plano.

Comentarios
  1. Estos principios están muy bien, pero es más fácil leerlos y entenderlos que llevarlos a la práctica. De hecho, creo que se necesita bastante experiencia y, sobre todo, haber leído mucho código fuente de otras personas que sepan programar bien, para que luego seas capaz de implementarlo. Además, por mi experiencia, no siempre es factible esta aplicación, aunque sí, intento que todo sea la más abstracto posible y que todas las clases sean lo más independientes posibles las unas de las otras. Otra cosa es que lo consiga 😉 .

    Responder

    • Coincido, no es nada sencillo.

      Peter Norvig se queda con corto con su “Teach Yourself Programming in Ten Years” 😛

      Responder

    • Angel

      Efectivamente, se necesita de experiencia para conseguir que estos principios SOLID manen de uno con cierta naturalidad. Pero en la vida real con el aliento de las entregas en la nuca,es normal que a uno se le escape alguno de estos principios.

      También se puede caer en la obsesión analítica, por dejar el diseño demasiado abierto en aspectos que claramente no se van a extender, a priori. Hay que llegar a un compromiso entre calidad en el mantenimiento y plazos. ¿Refactorizacion despues de llegar al hito de entrega?.

      Pero volvemos al mundo real, el cual dicta, que si tienes más proyectos encolados debes de cambiar tal como haces la entrega. Y no se refactoriza. Ese error, hace que lo que se puede hacer en un tiempo X al retomar el proyecto posteriormente se pase a 3X y se vaya engrandando aún más la complejidad en posteriores mantenimientos.

      A más experiencia, menor es la refactorización necesaria. Pero hay un coste inicial hasta que se tiene soltura y contra antes se asuma, mejor.

      Responder

  2. Luis

    Buen post, me es de mucha utilidad porque justamente estoy leyendo sobre patrones y formas de mejorar mi forma de programar. Gracias!!

    Responder

  3. […] Principios SOLID de la orientación a objetos. […]

    Responder

  4. […] diferenciadas. Comienza con un repaso básico de la orientación a objetos, explicando los 5 principios SOLID y algunos patrones de diseño clásicos como Strategy, Decorator o Composite; continúa con pruebas […]

    Responder

  5. […] que ser aún más cortas. También habla de la necesidad de que cada función haga una sola cosa (SRP), de limitar el nivel de anidamiento y el número de parámetros, o de por qué los parámetros […]

    Responder

  6. […] resulte difícil trabajar con ella. El fan-out es una medida de complejidad, muy relacionado con el Principio de Responsabilidad Única. Etiquetas: desarrollo, diseño, ingenieria, principios, programacion, Software […]

    Responder

  7. […] Al contrario, un buen diseño cuenta con un fan-out bajo, idealmente de 7±2, que es, según George Miller, uno de los mayores exponentes de la psicología cognitiva, el número máximo de elementos que una persona normal puede almacenar en su memoria a corto plazo. Con un fan-out bajo nos aseguramos de que la clase es lo bastante sencilla para que no nos resulte difícil trabajar con ella. El fan-out es una medida de complejidad, muy relacionado con el Principio de Responsabilidad Única. […]

    Responder

  8. […] Si usamos @ViewById para inyectar la vistas, por la manera que tiene de funcionar AndroidAnnotations, no tendremos todavía las instancias asociadas en onCreate, por lo que si tenemos que hacer algo con las vistas tendremos que anotar uno o varios métodos con @AfterViews. Esto, que a alguno le puede parecer un incordio, a mí personalmente me gusta, porque prefiero extraer el resto del código a otros métodos siguiendo el Principio de Responsabilidad Única. […]

    Responder

Deja un comentario