Dominando las recetas para el desarrollo efectivo de un proyecto de software, traté de encontrar por mí mismo las razones que hacen que sea útil utilizar los principios de desarrollo de la arquitectura SOLID (artículo Cómo no entender los principios de desarrollo de la arquitectura SOLID ).
Un análisis de estos principios permitió identificar varios patrones clave y elementos básicos que existen en el desarrollo. Nos permitieron describir, comprender e implementar SOLID en el trabajo real con un proyecto de software.
Se hizo interesante realizar un análisis de la aplicabilidad de estos conceptos para paradigmas de programación generalmente aceptados, por ejemplo, para OOP. Bueno, si el resultado de este trabajo te será útil.

Hoy en día, existen muchos enfoques para el diseño y la posterior implementación de proyectos de software. Los más demandados para trabajar con grandes proyectos de software son: programación estructural , programación funcional , programación orientada a objetos .
Para mí, se volvió interesante analizar las causas de estos enfoques de diseño. Y en el proceso de análisis, el descubrimiento inesperado fue el hecho de que todos están implícitamente basados en la siguiente premisa:
, .
Desarrollo de proyectos de software
¿Qué es un proyecto sin necesidad de desarrollo? Tales proyectos rara vez se encuentran y se caracterizan principalmente por un pago rápido a destajo sin ninguna obligación posterior por parte del programador, por ejemplo:
- un pequeño proyecto que se puede escribir con un enfoque;
- Un proyecto sin código estructuralmente complejo, cargado con una gran cantidad de relaciones;
- producto de software sin la necesidad de su soporte y soporte de usuario.
En tales situaciones, se desperdician los esfuerzos del programador para mantener, por ejemplo, un enfoque orientado a objetos. A menudo sucede que me encuentro en una lección sin sentido durante el desarrollo de una utilidad de consola única, cuando de repente me doy cuenta de que escribir el texto de 4to grado en este proyecto me retrasó por 15 minutos y no me acercó al resultado. Lo más triste es que todas las clases que apenas se escribieron en tales proyectos se olvidan y no se reutilizan, es decir, no facilitan nuestro trabajo en el futuro.
En todas las demás situaciones, el programador, minimizando su trabajo, debe desarrollar un proyecto estructuralmente complejo, es decir:
- Corrija los errores analizando el código y encontrando los lugares donde se generan estos errores.
- Introducir nuevas funcionalidades, manteniendo la funcionalidad de todas las capacidades disponibles anteriormente. Al hacerlo, utilice el código existente (escrito y probado) en la implementación de estas nuevas tareas.
- Brindar asistencia en el uso de un producto de software.
- Realice una descripción y coordinación de la funcionalidad de todas las versiones del proyecto.
- Mantenga todos los formatos de datos utilizados por el proyecto (incluso obsoletos) operativos.
- Y realice muchas otras tareas que aparecen en la confrontación con competidores causada por cambios en los marcos o el fin del soporte para SO obsoletos ...
Si busca analogías para el desarrollo de un proyecto de software, puede recordar la evolución de una especie biológica.
"". - . - .
El trabajo del programador no es fácil, pero el programador tiene un "ayudante". Este ayudante está oculto en algún lugar profundo de la estructura de nuestro mundo, en el que hay dos características:
- la capacidad de escribir un algoritmo útil y usarlo para muchas tareas similares,
- La presencia de una gran cantidad de tareas similares en su solución.
Este algoritmo, útil en muchos campos, se denominará algoritmo universal por brevedad. Su implementación para un campo de aplicación específico puede llamarse especialización, ya que el proceso de refinación del algoritmo para su uso en un campo de aplicación estrecho es similar a la especialización evolutiva de las células en un organismo vivo.
Obviamente, para crear un algoritmo, es necesario identificar características que aseguren la aplicabilidad del algoritmo. Estos signos deben buscarse en los datos de entrada y en la descripción de la situación inicial (contexto). Para crear un algoritmo universal , es necesario en cada área temática, que tiene sus propios conjuntos de signos de datos y situaciones, identificar signos de aplicabilidad que sean idénticos para todas las áreas. El algoritmo universal ignora todos los demás signos que no proporcionan aplicabilidad. Formalizando el algoritmo universal , hemos llegado a la necesidad de utilizar la abstracción , uno de los principios más importantes de la POO. Además, OOP se caracteriza por un énfasis solo en la abstracción de datos.
Aquí intentaré escribir ejemplos de cómo usar la abstracción de diferentes áreas.
Abstracción | Algoritmos | Campo de aplicación |
---|
Números naturales | Algoritmos de cálculo cuantitativo | Tareas de contabilidad de valores económicos |
Masa característica del cuerpo material | Algoritmos para comparar la cantidad de sustancia | Tareas de comparar el valor de un producto no responsable |
Interfaz con operaciones para una colección de elementos: rastreo completo, comparación e intercambio de posiciones | Algoritmos de clasificación de colecciones | Programacion |
La interfaz de las mismas operaciones para el "nodo final" y el "nodo de rama" en el árbol | Algoritmos basados en el patrón de diseño de diseño | Desarrollo de un proyecto de software complejo. |
Concepto clave "Empleado" | Redacción en la sección "Contrato de trabajo" | Código laboral |
Bloque de construcción de un proyecto de software
Usando varias técnicas de abstracción, el programador implementa el algoritmo en forma de una sección de código, que es un elemento separado y completo de su trabajo. Este elemento, dependiendo del lenguaje de programación utilizado, puede ser una función, un objeto y una secuencia de instrucciones. Para una mayor discusión, llamaremos a este fragmento de código la palabra " componente ".
Componente : un fragmento de código (procedimiento, clase, componente de implementación, etc.):
- que implementa un algoritmo completo que funciona en ciertas situaciones iniciales y con ciertos datos de entrada,
- que se puede usar varias veces en un proyecto (incluso mejor muchas veces en diferentes proyectos),
- todas las instrucciones están ubicadas cerca y vistas sin la necesidad de operaciones de búsqueda adicionales en el entorno de desarrollo,
- cambios en los que el programador se desempeña de manera relativamente independiente con respecto al resto del código.
Patrones en el desarrollo de un proyecto de software.
Usando el término componente , se hace posible formular un conjunto de leyes simples que existen en el desarrollo de un proyecto de software. Presentaré estos patrones en la forma de las siguientes declaraciones, divididas en 3 categorías.
- Declaraciones que describen las propiedades de un componente .
1.1. Un componente escrito correctamente se usa necesariamente y más a menudo varias veces.
1.2. En todos los lugares donde se usa el componente , se espera un comportamiento constante del mismo, lo que conduce a un resultado repetible.
1.3. Cuando se usa el componente en varios lugares, el resultado debe satisfacer cada lugar de uso.
1.4. El comportamiento incrustado en el componente crea restricciones en los lugares de uso de este componente .
1.5. En cada lugar de uso del componente , todas sus restricciones pueden estar involucradas.
1.6. Cualquier cambio en un componente cambia sus limitaciones y requiere la verificación de todos los lugares de su uso, lo que hace que un programador pierda tiempo.
1.7. Es aconsejable escribir el componente en forma de código en una instancia, es decir, es necesario eliminar la duplicación del mismo código. Esto reducirá el número de ediciones al hacer un cambio en un componente . - Declaraciones que describen patrones en la implementación de una nueva tarea por parte del programador.
2.1 Es aconsejable elegir una opción para implementar una nueva tarea mientras se minimiza el tiempo dedicado por el programador.
2.2. Para implementar una nueva tarea, un programador puede agregar nuevos componentes o cambiar el comportamiento de los componentes antiguos.
2.3. Agregar un componente básicamente requiere verificar solo en el lugar de nuevo uso, y genera un tiempo mínimo para el programador.
2.4. Según la declaración [1.6], un cambio en el comportamiento de un componente causado por una nueva tarea requiere verificación en el lugar de uso nuevo y en todos los lugares de uso anterior, lo que crea costos adicionales de tiempo del programador en comparación con la situación en la declaración [2.3]. En el caso de un componente publicado , esto requiere el trabajo de todos los programadores que usan el componente modificado. - Declaraciones que describen patrones en la interacción de algoritmos universales y sus especializaciones:
3.1. Existe la oportunidad de escribir un componente base (el nombre se introduce por analogía con la clase base y por razones de brevedad usaremos la palabra " base "). La base cumple solo las características más importantes de algunos algoritmos universales .
3.2. Es posible escribir un componente: especialización (en adelante, por brevedad, usaremos la palabra " especialización "). La especialización complementa el algoritmo universal de la base , haciéndolo aplicable en un área particular de uso.
3.3. La base , como se desprende de las declaraciones [3.1], [3.2], tiene menos complejidad y menos restricciones de aplicación que la especialización .
3.4. Según la declaración [1.7], es aconsejable desarrollar una especialización sin duplicación del código del algoritmo universal de la base de datos .
3.5. Los lugares de uso de la base de datos no requieren verificación después de realizar cambios en la especialización formada correctamente.
Conceptos de programación orientada a objetos
Intentaré, utilizando las declaraciones anteriores, analizar los conceptos básicos de la programación orientada a objetos. Este análisis omite el concepto de abstracción , ya que ya se ha descrito anteriormente en la formalización del método de construcción de un algoritmo universal .
Clase, objeto
Estos conceptos de OOP refuerzan la viabilidad de usar un tipo especial de componente descrito por una combinación de algunos datos internos y métodos para trabajar con estos datos. Todas las declaraciones del grupo [1] y [2] se traducen a OOP, para las cuales el término componente se reemplaza por el concepto de clase .
Al mismo tiempo, a primera vista, las relaciones de una clase y un objeto se agotan por el grupo de declaraciones [3], en el que la base se reemplaza por el concepto de clase , y la implementación por el concepto de un objeto . Además, la implementación es dinámica, es decir, modificable durante la ejecución del programa.
Encapsulación
El concepto de " encapsulación " puede considerarse desde dos "lados".
El primer lado del concepto de " encapsulación " es el aislamiento del componente de otras partes del código. Esta propiedad permite al programador realizar operaciones en áreas del código que se encuentran "cerca" para realizar cambios en el componente . Es decir, para minimizar el tiempo dedicado por el programador al excluir del trabajo la búsqueda y el análisis de elementos interactivos dispares del programa. Este lado está definido por las propiedades del componente que sigue a su definición.
El segundo lado del concepto de " encapsulación " es el ocultamiento de la implementación interna del componente . Esta ocultación es posible utilizando los conceptos de base e implementación descritos en el grupo de declaraciones [3]. Para hacer esto, los métodos de clase pública se identifican con la base , y los métodos de clase privados y protegidos se identifican con la implementación . En los lugares de uso, se utilizan las restricciones formadas por la base y, por lo tanto, es posible realizar cambios en la implementación que no están relacionados con las restricciones básicas . Y estos cambios de implementación no necesitan ser verificados en los lugares donde se usa la base de datos [3.5], lo que minimiza el trabajo del programador.
Es de destacar que el concepto de " encapsulación " tiene una analogía en biología. Este primer proceso es similar a las funciones biológicas de la " membrana celular ".
Herencia
El concepto de " herencia " continúa reforzando la importancia de utilizar una combinación de base + implementación . Para esto, en el grupo de declaraciones [3] es necesario identificar los métodos de la clase padre con la base , e identificar los métodos de la clase sucesora con la implementación .
En su implementación, el concepto de “ herencia ” permite el uso de la declaración [2.3], es decir, usar la adición de código en lugar de cambiarlo y duplicarlo. En este caso, es necesario excluir la duplicación del algoritmo básico . Sin embargo, un enfoque que utiliza la herencia para especializar un algoritmo universal tiene una desventaja significativa. Esta desventaja es la presencia de dos componentes fuertemente conectados, que son difíciles de cambiar de forma independiente. Estas relaciones de dependencia son generadas por la relación padre-hijo.
Hay muchas formas alternativas de usar el paquete de implementación base +. Daré más ejemplos de tales métodos.
Base | Implementación | Campo de aplicación |
---|
Métodos de clase pública | Métodos de clase privada | Encapsulación |
Métodos protegidos de la clase padre. | Métodos de clase de herencia | Herencia |
Interfaz de biblioteca dinámica | Funcionalidad de biblioteca dinámica | Componente = biblioteca dinámica |
Métodos y clases de plantilla (generalizada) (plantilla, genérica) | Crear instancias de una plantilla con argumentos especificados | Programación general |
Métodos genéricos que aceptan delegados. | Especialización de métodos que indican procedimientos de procesamiento específicos. | Procedimientos para ordenar o formar un árbol, indicando el método para evaluar el orden de los elementos. |
Clases que permiten la interacción con la plantilla de visitante | Formación de "Visitante" con la funcionalidad requerida | Patrón de diseño de visitante |
Panel de control de NPP | El conjunto de automatización y equipamiento de centrales nucleares. | Ocultación de la complejidad del sistema por parte del operador de la central nuclear. |
Al mismo tiempo, noto que para el concepto de " herencia " de la OLP, también se puede encontrar una analogía en los procesos de evolución biológica. En biología, el término " herencia " se utiliza para esto.
Polimorfismo
En mi opinión, el concepto de " polimorfismo " es el segundo lado cuando se observa el procedimiento para crear un algoritmo universal . El primer lado ( abstracción ) es una vista desde el punto de vista de cómo crear un algoritmo universal . Al mismo tiempo, cuando observamos el algoritmo universal desde el punto de vista del usuario, obtenemos un registro del concepto de polimorfismo . Es decir, el polimorfismo es una capacidad útil de una función ( componente ) para procesar datos de varios tipos. Agregar este concepto a OOP fortalece la utilidad del uso de un algoritmo universal en el desarrollo de un proyecto de software.
Las implementaciones de polimorfismo en diferentes lenguajes de programación son muy diferentes. En el artículo de Wikipedia para el polimorfismo , dependiendo de su implementación, hay 4 subtipos: paramétrico, inclusión (o subtipos), sobrecarga, conversión de tipos. Estas implementaciones tienen diferencias significativas, pero todas están unidas por un objetivo: escribir un algoritmo universal que no necesitará duplicarse para su especialización específica.
Y esta vez, casi sin sorpresa, encontró una analogía para el concepto de " polimorfismo " en biología. El nombre de este término biológico coincide totalmente con el concepto de OOP. " Polimorfismo ": la capacidad de un organismo para existir en estados con diferentes estructuras internas o en diferentes formas externas.
Conclusión
Por lo tanto, casi todos los conceptos básicos de OOP se pueden representar como un conjunto de declaraciones simples formadas sobre la base de las leyes de desarrollo de un proyecto de software. Además, para OOP, el término componente se identifica con el concepto de una clase . Si seleccionamos un significado diferente para el término componente , por ejemplo, una función , entonces es posible formular los conceptos básicos de la programación funcional .
En el proceso de redacción del artículo, se encontraron analogías biológicas para los conceptos utilizados en la programación. Estas analogías aparecen debido a la similitud de los métodos de desarrollo de un producto de software y algunos procesos de evolución biológica.
En mi humilde opinión, es aconsejable considerar estas dos áreas científicas juntas. En este caso, puede ser posible llevar a cabo la transferencia de leyes de una industria a otra, y así garantizar el desarrollo de la tecnología de la información y las descripciones formales de los procesos biológicos.
Gracias por su atencion
Comentarios
Estaría muy agradecido por los comentarios, sugerencias y sugerencias, ya que me ayudan a ajustar la dirección del desarrollo del trabajo en esta área.
Referencias
Editado por Borisova M.V.