Hay un problema con la descripción e interpretación de los principios de desarrollo de la arquitectura SÓLIDA (autoría de Robert Martin). Muchas fuentes dan su definición e incluso ejemplos de su uso. Al estudiarlos e intentar usarme a mí mismo, constantemente me sorprendía pensando que no había suficiente explicación sobre la magia de su aplicación. Y tratando de ver los engranajes internos, de entender, y para mí significa recordar, los colocó en sus "estantes de términos". Bueno, si será útil para alguien más.

Procedemos a "hacer malabarismos con los estantes" del enfoque de diseño anterior.
Principio de responsabilidad única (SRP) principio de responsabilidad única
Un código debe cambiar solo durante la implementación de un objetivo. Si una sección de código implementa dos tareas y cambios para diferentes usos, entonces debe duplicar esta sección en una instancia para cada propósito. Esto es muy importante porque requiere una desviación del principio generalmente aceptado de eliminar la duplicación.
El propósito de este principio es eliminar los errores implícitos introducidos debido al hecho de que existen los siguientes invariantes para el desarrollo de una sección de código, procedimiento, clase, componente (en adelante, el término [componente] se usa para combinar estos conceptos):
- [1] escrito correctamente [componente] se usa necesariamente y más a menudo varias veces,
- [2] en cada lugar de uso, se espera que [componente] mantenga un comportamiento consistente que conduzca a un resultado repetible,
- [3] cuando se utiliza el [componente] en varios lugares, el resultado debe satisfacer cada lugar de uso,
- si se requiere un cambio en [componente] para uno de los lugares de uso, y el comportamiento anterior de [componente] se requiere para otro lugar de uso, es necesario crear una copia de [componente] y luego modificarlo (o generalizar [componente] con parámetros adicionales que proporcionan un comportamiento diferente),
si hay lugares de uso del [componente] que no son importantes para la tarea actual resuelta por el programador, entonces es muy fácil para él olvidarse de verificar la compatibilidad con estos lugares de uso de los cambios realizados en este [componente].
Por lo tanto, todos los lugares de uso deben ubicarse en la zona [Responsabilidad única] de una responsabilidad única, es decir, cambiarse y tenerse en cuenta de inmediato para cualquier problema resuelto por el programador).
El principio se aplica tanto a una sección de código como a un componente, biblioteca, programa, conjunto de programas utilizados en varios lugares.
Muchas fuentes dan un ejemplo de una clase con una sola "función" como el ideal de SRP y la clase del "objeto divino", que combina todas las funciones de la aplicación, como antipatrón. Una clase IMHO con una sola "función" es un requisito para la optimización prematura de la arquitectura de código, lo que lleva a escribir muchas clases (entidades de código) desde cero, mientras olvida que la ausencia de más de un lugar de uso permite al programador evaluar rápidamente una pequeña cantidad localmente localizada (en una clase) código de interacción que analizar las relaciones externas de entidades de código dispares responsables de su "función". Un "objeto divino" para una aplicación pequeña tampoco es un delito fuerte: le permite comenzar el desarrollo: seleccione todas las entidades necesarias y, escritas una al lado de la otra, separe los objetos externos de la biblioteca estándar y los módulos externos (cree una célula viva y sepárela con una membrana). En el proceso de crecimiento y desarrollo del proyecto, existen muchos métodos que ayudan a seguir el SRP, uno de ellos es la división en clases y minimizar el número de "funciones" de las que cada clase es responsable (división celular y su especialización en el cuerpo).
Aquí me gustaría escribir un conjunto de técnicas para mantener SRP, pero este trabajo aún no se ha completado (espero que "alcancen las manos"). Desde las áreas obvias donde puedes buscar estos trucos:
- patrones de diseño;
- utilizando diferentes ramas de componentes especializados, en lugar de crear un componente que satisfaga todos los métodos de aplicación (fork en GitHub).
Principio abierto-cerrado (OCP) Principio abierto / cerrado
Es óptimo planificar el desarrollo del código de modo que para que el programador implemente nuevas tareas sea necesario agregar un nuevo código, mientras que el código antiguo no necesita cambios. El código debe estar abierto (Abrir) para agregar y cerrado (Cerrado) para cambiar.
El propósito de este principio es minimizar los costos laborales y eliminar los errores implícitos introducidos debido a los siguientes invariantes en el desarrollo:
- [1], [2], [3] descrito anteriormente,
- para implementar una nueva tarea, el programador puede agregar [componentes] nuevos o cambiar el comportamiento de los [componentes] antiguos,
- la adición de [componente] requiere verificación en el lugar de nuevo uso y le cuesta tiempo al programador
- El cambio en el comportamiento del [componente] causado por la nueva tarea requiere verificación en el lugar de uso nuevo y en todos los lugares de uso anterior, lo que también causa el consumo de tiempo del programador y, en el caso del [componente] publicado, el trabajo de todos los programadores que usaron el [componente].
Es aconsejable elegir una opción para implementar una nueva tarea mientras se minimiza el tiempo dedicado por el programador.
Con mayor frecuencia en la práctica del desarrollo de software, el costo de agregar es mucho menor que el costo de cambiar, lo que hace obvio el beneficio de usar el principio [Abierto-Cerrado]. Al mismo tiempo, hay muchas técnicas para mantener la arquitectura del programa en un estado donde la implementación de una nueva tarea se reduce a agregar solo [componentes]. Este trabajo con la arquitectura también requiere el tiempo de un programador, pero la práctica en proyectos grandes muestra mucho menos que usar el enfoque de cambiar procedimientos antiguos. Y, por supuesto, esta descripción del desarrollo es idealización. Casi no hay implementación de la tarea simplemente agregando o simplemente cambiando. En la vida real, se usa una combinación de estos enfoques, pero OCP enfatiza el beneficio de usar el enfoque de agregar.
Y aquí me gustaría escribir un conjunto de técnicas para mantener OCP. Desde las áreas obvias donde puedes buscar estos trucos:
- patrones de diseño;
- dll bibliotecas y opciones para su distribución, actualización y desarrollo de funcionalidad;
- desarrollo de bibliotecas COM y objetos en ellas;
- desarrollo de lenguajes de programación y soporte para código previamente escrito;
- desarrollar el sistema legislativo del estado.
Principio de sustitución de Liskov (LSP) Barbara Principio de sustitución de Liskov
Este principio limita el uso de la extensión de la interfaz base [base] a la implementación, indicando que cada implementación de la interfaz base debe tener un comportamiento como interfaz base. Al mismo tiempo, la interfaz básica corrige el comportamiento esperado en los lugares de su uso. Y la presencia en el comportamiento de implementación de una diferencia del comportamiento esperado, corregido por la interfaz base, conducirá a la posibilidad de violación de la invariante [2].
Este principio se basa y refina la técnica de diseño basada en la abstracción. En este enfoque, se introduce una abstracción: se corrigen algunas propiedades básicas y el comportamiento característico de muchas situaciones. Por ejemplo, [componente-procedimiento] "Mover a la posición anterior" para situaciones: "Cursor en texto", "Libro en un estante", "Elemento en una matriz", "Pies en danza", etc. Y asignado a este [componente] ( a menudo por experiencia cotidiana y sin formalización) algunos requisitos previos y comportamiento, por ejemplo: "La presencia de un objeto móvil", "Repetir varias veces", "Presencia del orden de los elementos", "Presencia de posiciones fijas de los elementos". LSP requiere que al agregar una nueva situación de uso para [componente] se cumplan todos los requisitos previos y limitaciones de la base. Y esta situación no puede describir la situación de "granos en una lata de azúcar", aunque los granos, por supuesto, tienen una posición, hay posiciones en las que los granos han estado antes, y es posible moverla en ellos, solo que no hay posiciones fijas de elementos.
El propósito de este principio es eliminar los errores implícitos introducidos debido a los siguientes invariantes en desarrollo:
- [1], [2], [3] descrito anteriormente,
- el [procedimiento] básico describe un comportamiento que es útil en una gran cantidad de situaciones, estableciendo las restricciones requeridas para su aplicabilidad,
El [procedimiento] desarrollado para la implementación de la base debe cumplir con todas sus limitaciones, incluidas las implícitas (siempre de manera informal).
Muy a menudo, se da un ejemplo con un Rectángulo ([base]) y un Cuadrado (implementación) para describir este principio. class CSquare : public CRectangle
situación class CSquare : public CRectangle
. En [base], se introducen las operaciones para trabajar con ancho y alto (Establecer (Obtener) Ancho, Establecer (Obtener) Altura). En la implementación de CSquare, estas operaciones Set se ven obligadas a cambiar ambos tamaños del objeto. Siempre me faltó la explicación de que la siguiente restricción se establece "informalmente" en [la base]: "la capacidad de usar Ancho, Altura independientemente". En la implementación de CSquare, se viola, y en los lugares de uso, una secuencia simple de acciones basadas en el uso de esta independencia: r.SetWidth(r.GetWidth()*2); r.SetHeight(r.GetHeight()*2)
r.SetWidth(r.GetWidth()*2); r.SetHeight(r.GetHeight()*2)
: para la implementación, CSquare aumentará ambos tamaños en 4 veces, en lugar de 2 veces se supone para CRectangle.
En mi humilde opinión, este principio indica la dificultad de rastrear tales restricciones informales, que, con gran utilidad y alta frecuencia de uso del enfoque de desarrollo "base-implementación" requiere atención especial.
Principio del principio de segregación de interfaz (ISP) de separación de interfaces; Principio de inversión de dependencia (DIP) Principio de inversión de dependencia
Estos dos principios son muy cercanos en el área de sus requisitos. Ambos implican implícitamente la utilidad de utilizar la interfaz básica más pequeña posible como herramienta para la interacción de dos [componentes]: "cliente" y "servidor": estos nombres se eligen simplemente para su identificación. En este caso, la información general utilizada por los [componentes] se concentra en la interfaz base. Un [componente] ("servidor") implementa la implementación de la interfaz base, el otro [componente] ("cliente") se refiere a esta implementación.
El objetivo de estos principios es minimizar las dependencias de los componentes, permitiendo cambios independientes en su código si no cambia la interfaz subyacente. La independencia de los cambios de componentes reduce la complejidad y la mano de obra si los componentes cumplen con los requisitos del principio SRP. Es posible un enfoque similar porque existen los siguientes invariantes en desarrollo:
- [1], [2], [3] descrito anteriormente,
- cada [componente] inherente a su comportamiento forma los límites de su uso,
- en cada lugar de uso del [componente] pueden estar involucradas todas sus restricciones,
- la consecuencia básica [componente] de la definición tiene menos complejidad y el número de restricciones que la implementación [componente],
- cualquier cambio en [componente] cambia sus limitaciones y requiere la verificación de todos los lugares de uso, lo que ocasiona el gasto de tiempo del programador,
los lugares de uso de la base [componente] no requieren verificación después de realizar cambios en la implementación [componente].
Está claro que es aconsejable minimizar el "tamaño" de la interfaz base descartando la funcionalidad y las restricciones no utilizadas, limitando así la implementación [componente] por el principio de (LSP) menos
El principio de ISP enfatiza la necesidad de separación (segregación) de la interfaz del "servidor", si este "cliente" no utiliza toda su funcionalidad publicada. En este caso, solo se asigna la [base] requerida por el cliente y se garantiza la minimización de la información de restricción conjunta.
Y aquí me gustaría escribir un conjunto de técnicas para mantener DIP. Desde las áreas obvias donde puedes buscar estos trucos:
- separación de la descripción de la clase en partes públicas y privadas (y otros principios de OOP),
- Descripción de la interacción con una biblioteca dinámica con un conjunto limitado de funciones y descriptores de objetos.
- utilizando un archivador como interfaz para acceder a una biblioteca de libros.
Volviendo al encabezado, explicaré por qué se selecciona "no entender". Se agrega la negación para enfatizar, por errores, la regla útil muy duradera y muy útil de mi humilde opinión. Es mejor no entender y, por lo tanto, no usar la tecnología, que malinterpretar, confiar en ella, gastar sus recursos en la aplicación de la tecnología y, como resultado, no obtener ningún escape útil, excepto la complacencia y la posibilidad de jactarse de estar involucrado en la tecnología de moda.
Gracias por su atencion
Referencias