La responsabilidad única (SRP), abierta / cerrada (OCP), sustitución de Liskov, segregación de interfaz y Inversión de dependencia. Cinco principios ágiles que deberían guiarte cada vez que escribas un código..
Sería injusto decirle que cualquiera de los principios de SOLID es más importante que otro. Sin embargo, probablemente ninguno de los otros tenga un efecto tan inmediato y profundo en su código como el Principio de Inversión de Dependencia, o DIP en breve. Si encuentra que los otros principios son difíciles de entender o aplicar, comience con este y aplique el resto en el código que ya respeta DIP.
A. Los módulos de alto nivel no deben depender de módulos de bajo nivel. Ambos deben depender de las abstracciones..
B. Las abstracciones no deben depender de los detalles. Los detalles deben depender de las abstracciones..
Este principio fue definido por Robert C. Martin en su libro Agile Software Development, Principles, Patterns, and Practices y luego publicado de nuevo en la versión C # del libro Agile Principles, Patterns, and Practices in C #, y es el último de los cinco. Sólidos principios ágiles..
Antes de comenzar a programar, me gustaría contarles una historia. En Syneto, no siempre fuimos muy cuidadosos con nuestro código. Hace unos años sabíamos menos y aunque intentamos dar lo mejor de nosotros, no todos nuestros proyectos fueron tan buenos. Pasamos por el infierno y volvimos y aprendimos muchas cosas por ensayo y error..
Los principios de SOLID y los principios de arquitectura limpia del tío Bob (Robert C. Martin) se convirtieron en un cambio de juego para nosotros y transformaron nuestra forma de codificación de una manera que es difícil de describir. Intentaré ejemplificar, en pocas palabras, algunas decisiones arquitectónicas clave impuestas por el DIP que tuvieron un gran impacto en nuestros proyectos..
La mayoría de los proyectos web contienen tres tecnologías principales: HTML, PHP y SQL. La versión particular de estas aplicaciones de las que estamos hablando o el tipo de implementaciones de SQL que utiliza es irrelevante. La cuestión es que la información de un formulario HTML debe terminar, de una forma u otra, en la base de datos. El pegamento entre los dos se puede proporcionar con PHP.
Lo que es esencial quitar de esto es que las tres tecnologías representan tres capas arquitectónicas diferentes: interfaz de usuario, lógica de negocios y persistencia. Hablaremos sobre las implicaciones de estas capas en un minuto. Por ahora, concentrémonos en algunas soluciones extrañas pero frecuentes para hacer que las tecnologías funcionen juntas..
Muchas veces he visto proyectos que usaban código SQL en una etiqueta PHP dentro de un archivo HTML, o código PHP haciendo eco de páginas y páginas de HTML e interpretando directamente el $ _GET
o $ _POST
variables globales. Pero ¿por qué es tan malo??
Las imágenes de arriba representan una versión en bruto de lo que describimos en el párrafo anterior. Las flechas representan varias dependencias, y como podemos concluir, básicamente todo depende de todo. Si necesitamos cambiar una tabla de base de datos, podemos terminar editando un archivo HTML. O si cambiamos un campo en HTML, podemos terminar cambiando el nombre de una columna en una declaración SQL. O si observamos el segundo esquema, es posible que tengamos que modificar nuestro PHP si el HTML cambia, o en casos muy graves, cuando generemos todo el contenido HTML desde un archivo PHP, seguramente necesitaremos cambiar un archivo PHP Modificar el contenido HTML. Entonces, no hay duda, las dependencias están en zigzag entre las clases y los módulos. Pero no termina aquí. Puede almacenar procedimientos; Código PHP en tablas SQL.
En el esquema anterior, las consultas a la base de datos SQL devuelven el código PHP generado con datos de las tablas. Estas funciones o clases de PHP están realizando otras consultas SQL que devuelven códigos PHP diferentes, y el ciclo continúa hasta que finalmente se obtiene y devuelve toda la información ... probablemente a la interfaz de usuario.
Sé que esto puede parecer escandaloso para muchos de ustedes, pero si aún no ha trabajado con un proyecto inventado e implementado de esta manera, seguramente lo hará en su futura carrera. La mayoría de los proyectos existentes, independientemente de los lenguajes de programación utilizados, fueron escritos con principios anteriores en mente, por programadores a los que no les importaba o no sabían lo suficiente como para hacerlo mejor. Si estás leyendo estos tutoriales, lo más probable es que estés en un nivel más alto. Estás listo o te estás preparando para respetar tu profesión, para abrazar tu oficio y para hacerlo mejor..
La otra opción es repetir los errores que cometieron sus antecesores y vivir con las consecuencias. En Syneto, después de que uno de nuestros proyectos alcanzó un estado casi imposible de mantener debido a su arquitectura antigua y dependiente de todos y tuvimos que abandonarlo para siempre, decidimos no volver a recorrer ese camino. Desde entonces, nos hemos esforzado por tener una arquitectura limpia que respete correctamente los principios de SOLID y, lo más importante, el principio de inversión de dependencia..
Lo sorprendente de esta arquitectura es cómo apuntan las dependencias:
La aplicación del principio de inversión de dependencia (DIP) a nivel arquitectónico es bastante fácil si respeta los patrones de diseño ágil clásico. Ejercitarlo y ejemplificarlo dentro de la lógica de negocios también es bastante fácil e incluso puede ser divertido. Imaginaremos una aplicación de lector de libros electrónicos..
class Test extiende PHPUnit_Framework_TestCase function testItCanReadAPDFBook () $ b = new PDFBook (); $ r = nuevo PDFReader ($ b); $ this-> assertRegExp ('/ pdf book /', $ r-> read ()); class PDFReader private $ book; función __construir (libro PDFBook $) $ this-> book = $ libro; función read () return $ this-> book-> read (); class PDFBook function read () return "leyendo un libro en pdf.";
Comenzamos a desarrollar nuestro e-reader como un lector de PDF. Hasta ahora tan bueno. Tenemos una Lector PDF
clase usando un Libro PDF
. los leer()
Función en el lector delegados a la del libro. leer()
método. Simplemente verificamos esto haciendo una verificación de expresiones regulares después de una parte clave de la cadena devuelta por Libro PDF
es lector()
método.
Por favor, tenga en cuenta que esto es sólo un ejemplo. No implementaremos la lógica de lectura de archivos PDF u otros formatos de archivo. Es por eso que nuestras pruebas simplemente comprobarán algunas cadenas básicas. Si tuviéramos que escribir la aplicación real, la única diferencia sería cómo probamos los diferentes formatos de archivo. La estructura de dependencia sería muy similar a nuestro ejemplo..
Tener un lector de PDF utilizando un libro PDF puede ser una solución sólida para una aplicación limitada. Si nuestro objetivo fuera escribir un lector de PDF y nada más, sería una solución aceptable. Pero queremos escribir un lector genérico de libros electrónicos que admita varios formatos, entre los cuales se encuentra nuestra primera versión en PDF. Vamos a cambiar el nombre de nuestra clase de lector.
class Test extiende PHPUnit_Framework_TestCase function testItCanReadAPDFBook () $ b = new PDFBook (); $ r = nuevo EBookReader ($ b); $ this-> assertRegExp ('/ pdf book /', $ r-> read ()); class EBookReader private $ book; función __construir (libro PDFBook $) $ this-> book = $ libro; función read () return $ this-> book-> read (); class PDFBook function read () return "leyendo un libro en pdf.";
El cambio de nombre no tenía efectos contrarios funcionales. Las pruebas siguen pasando.
Las pruebas comenzaron a la 1:04 pm ...
PHPUnit 3.7.28 por Sebastian Bergmann.
Tiempo: 13 ms, Memoria: 2.50Mb
OK (1 prueba, 1 aserción)
Proceso terminado con código de salida 0
Pero tiene un serio efecto de diseño..
Nuestro lector se volvió mucho más abstracto. Mucho más general. Tenemos un genérico Lector de libros electrónicos
que utiliza un tipo de libro muy específico, Libro PDF
. Una abstracción depende de un detalle. El hecho de que nuestro libro sea de tipo PDF debería ser solo un detalle, y nadie debería depender de él..
class Test extiende PHPUnit_Framework_TestCase function testItCanReadAPDFBook () $ b = new PDFBook (); $ r = nuevo EBookReader ($ b); $ this-> assertRegExp ('/ pdf book /', $ r-> read ()); interface EBook function read (); class EBookReader private $ book; function __construct (EBook $ book) $ this-> book = $ book; función read () return $ this-> book-> read (); clase PDFBook implementa EBook function read () return "leyendo un libro en pdf";
La solución más común y más utilizada para invertir la dependencia es introducir un módulo más abstracto en nuestro diseño. "El elemento más abstracto en OOP es una interfaz. Por lo tanto, cualquier otra clase puede depender de una interfaz y seguir respetando DIP"..
Creamos una interfaz para nuestro lector. La interfaz se llama Libro electronico
y representa las necesidades de la Lector de libros electrónicos
. Este es un resultado directo de respetar el Principio de Segregación de Interfaz (ISP) que promueve la idea de que las interfaces deben reflejar las necesidades de los clientes. Las interfaces pertenecen a los clientes y, por lo tanto, se nombran para reflejar los tipos y objetos que necesitan los clientes y contendrán los métodos que los clientes desean utilizar. Es natural para un Lector de libros electrónicos
usar Libros electrónicos
y tener un leer()
método.
En lugar de una sola dependencia, ahora tenemos dos dependencias.
Lector de libros electrónicos
hacia Libro electronico
Interfaz y es de uso tipográfico.. Lector de libros electrónicos
usos Libros electrónicos
.Libro PDF
hacia el mismo Libro electronico
Interfaz pero es de tipo implementación. UNA Libro PDF
es solo una forma particular de Libro electronico
, y así implementa esa interfaz para satisfacer las necesidades del cliente..Como era de esperar, esta solución también nos permite conectar diferentes tipos de libros electrónicos en nuestro lector. La única condición para todos estos libros es satisfacer la Libro electronico
Interfaz e implementarlo.
class Test extiende PHPUnit_Framework_TestCase function testItCanReadAPDFBook () $ b = new PDFBook (); $ r = nuevo EBookReader ($ b); $ this-> assertRegExp ('/ pdf book /', $ r-> read ()); function testItCanReadAMobiBook () $ b = new MobiBook (); $ r = nuevo EBookReader ($ b); $ this-> assertRegExp ('/ mobi book /', $ r-> read ()); interface EBook function read (); class EBookReader private $ book; function __construct (EBook $ book) $ this-> book = $ book; función read () return $ this-> book-> read (); clase PDFBook implementa EBook function read () return "leyendo un libro en pdf"; clase MobiBook implementa EBook function read () return "leyendo un libro mobi.";
Lo que a su vez nos lleva al principio abierto / cerrado, y el círculo está cerrado..
El principio de inversión de dependencia es uno que nos guía o nos ayuda a respetar todos los demás principios. Respetando DIP:
Eso es. Hemos terminado. Todos los tutoriales sobre los principios de SOLID están completos. Para mí, personalmente, descubrir estos principios e implementar proyectos con ellos en mente fue un gran cambio. Cambié completamente mi forma de pensar sobre el diseño y la arquitectura y puedo decir que desde entonces todos los proyectos en los que trabajo son exponencialmente más fáciles de gestionar y comprender..
Considero que los principios SOLID son uno de los conceptos más esenciales del diseño orientado a objetos. Estos conceptos que deben guiarnos para mejorar nuestro código y nuestra vida como programadores son mucho más fáciles. El código bien diseñado es más fácil de entender para los programadores. Las computadoras son inteligentes, pueden entender el código sin importar su complejidad. Los seres humanos, por otro lado, tienen un número limitado de cosas que pueden mantener en su mente activa y enfocada. Más específicamente, el número de tales cosas es el Número Mágico Siete, Más o Menos Dos.
Debemos esforzarnos por tener nuestro código estructurado en torno a estos números y existen varias técnicas que nos ayudan a hacerlo. Funciones con un máximo de cuatro líneas de longitud (cinco con la línea de definición incluida) para que todas puedan caber al mismo tiempo dentro de nuestra mente. Las sangrías no superan los cinco niveles de profundidad. Clases con no más de nueve métodos. Diseña patrones que usualmente usan un número de cinco a nueve clases. Nuestro diseño de alto nivel en los esquemas anteriores utiliza de cuatro a cinco conceptos. Hay cinco principios SOLID, cada uno de los cuales requiere de cinco a nueve sub-conceptos / módulos / clases para ser ejemplificados. El tamaño ideal de un equipo de programación es entre cinco y nueve. El número ideal de equipos en una empresa es entre cinco y nueve..
Como puede ver, el número mágico siete, más o menos dos está a nuestro alrededor, ¿por qué su código debería ser diferente??