¿Cómo decide dónde debe pertenecer la funcionalidad en un proyecto a gran escala?

8

En mi situación actual de desarrollo, tenemos muchas DLL, ejecutables y bibliotecas estáticas. ¿Cómo se decide qué debe incluir una DLL? ¿Qué debería ir en un ejecutable? ¿Por qué tener una funcionalidad separada en diferentes archivos ejecutables? Espero que la respuesta sea concisa, pero este es un tema en gran parte obstinado que parece.

¿Cómo decide dónde reside la funcionalidad en un proyecto a gran escala (más de un archivo ejecutable)? Espero que las respuestas oscilen entre "buen diseño", "modularidad" y "lo que sea que la gerencia ponga en un documento de requisitos".

wfoster
fuente

Respuestas:

13

Elegir entre una biblioteca y un archivo ejecutable es relativamente simple: ¿tiene sentido ejecutar el código que desea poner en un ejecutable como un programa independiente? Si no, probablemente debería ser una biblioteca. En general, preferiría una capa ejecutable delgada sobre tantas bibliotecas como sea necesario, ya que eso hace que sea más fácil reutilizar esas bibliotecas de fondo más tarde y no están vinculadas a un programa en particular.

En cuanto a decidir cómo dividir su código entre bibliotecas, puede encontrar útil el artículo del tío Bob Martin sobre granularidad .

En él habla sobre la estructura OO y define varios principios que pueden ayudarlo a empaquetar su código de manera adecuada. Estos también se cubren con más detalle en su libro, Principios ágiles, patrones y prácticas en C # .

Resumiré los principios a continuación:

El Principio de Equivalencia de Reutilización / Liberación (REP)

El gránulo de reutilización es el gránulo de liberación. Solo los componentes que se liberan a través de un sistema de seguimiento pueden reutilizarse de manera efectiva. Este gránulo es el paquete.

El tío Bob define la reutilización como la capacidad de vincular estática o dinámicamente la biblioteca reutilizada en su programa y nunca tener que mirar su código fuente. Cuando se lanza una nueva versión de la biblioteca, puede integrarla en su sistema.

El tratamiento de las bibliotecas de esta manera conduce solo a mantener las cosas relacionadas juntas en el mismo paquete. De lo contrario, los consumidores de la biblioteca podrían tener que actualizar a una nueva versión sin ningún motivo o retrasarse algunas versiones.

El principio de reutilización común (CRP)

Las clases en un paquete se reutilizan juntas. Si reutiliza una de las clases en un paquete, las reutiliza todas.

Este principio es compatible con el anterior. Si tiene clases en el mismo paquete que no están relacionadas entre sí, puede estar obligando a los usuarios de su biblioteca a actualizar innecesariamente.

El principio de cierre común (PCC)

Las clases en un paquete deben cerrarse contra los mismos tipos de cambios. Un cambio que afecta a un paquete afecta a todas las clases en ese paquete.

Este principio habla de mantenibilidad. La idea aquí es agrupar las clases en función de cómo podrían necesitar cambiar. De esa forma, sus cambios se pueden localizar en una parte de la aplicación y no extenderse por todas partes.

El Principio de Dependencias Acíclicas (ACP)

La estructura de dependencia entre paquetes debe ser un Gráfico Acíclico Dirigido (DAG). Es decir, no debe haber ciclos en la estructura de dependencia.

No permitir dependencias cíclicas permite que cada paquete se desarrolle de forma independiente y se "libere" al resto de la compañía cuando estén listos los nuevos cambios. De esta manera no terminas con dos equipos estancados, esperando el uno al otro para terminar un trabajo.

Adam Lear
fuente
2

¿Qué va a facilitar el mantenimiento del código a largo plazo? Si tiene funciones no relacionadas en el mismo componente (es decir, dll, exe, unidad compilada), y tiene que cambiar una función, ¿eso va a romper la otra? Cuando realice un cambio en un componente, deberá volver a probar todos los componentes posteriores en función de TODAS las funciones de ese componente. Si limita la funcionalidad dentro de cada componente, cambiar cada uno será menos riesgoso.

Además, al enfocar los componentes en un pequeño número de funcionalidades estrechamente relacionadas, le resultará mucho más fácil demostrar que ese componente se comporta como debería.

Matthew Flynn
fuente
Siempre piense en el escenario "DLL Hell". Así es como dijo el Sr. Flynn, piense qué sucede cuando algo cambia. Siempre conozca muy bien sus dependencias y asegúrese de que puede cubrir los escenarios de instalación sin dolor para el usuario final.
NoChance
2

Una forma sencilla de abordar una respuesta es reconocer que "DLL" significa "biblioteca de enlace dinámico", y el término clave es "biblioteca".

¿Qué quieres en una biblioteca normal? Buenos libros que serán compartidos por muchos lectores (usuarios); elementos útiles que acumulan polvo hasta que un lector (usuario) haga referencia para responder una pregunta crítica en un momento quizás desesperado; y recursos que vinculan a los lectores (usuarios) con otros recursos. Las bibliotecas de códigos son similares. Conservamos elementos como código compartido con frecuencia, módulos de referencia especializados o de alto valor, y recursos del marco arquitectónico en ellos. Las bibliotecas de software se pueden representar en varios tipos de artefactos de código, como scripts, bibliotecas estáticas, bibliotecas dinámicas, componentes y archivos de recursos.

En general, recomiendo hacer que sus módulos ejecutables actúen como scripts. Esbozan y administran claramente la estructura principal y el flujo de su sistema, pero recurren a los recursos de sus bibliotecas para manejar los detalles esenciales. Encuentro este enfoque mejor que enturbiar la lógica de alto nivel con preocupaciones de implementación de bajo nivel confusas y demasiado especializadas y técnicas.

Al considerar cómo asignar su código entre ejecutables y bibliotecas, debe considerar tanto el diseño lógico como el diseño físico.

En los sistemas orientados a objetos, por ejemplo, es importante organizar lógicamente su código para asignar correctamente las responsabilidades a los métodos correctos en las clases correctas. Esto es parte del diseño de solución lógica. Su diseño lógico debe ser claro, limpio y sencillo y debe expresarse en una terminología que se relacione bien con el dominio de sus usuarios.

Cuando planea instalar realmente su sistema en el sitio de un usuario, puede preocuparse por crear un diseño físico que especifique cómo agrupará su código en un conjunto de objetos de recursos de software fácilmente implementables (generalmente archivos) que se pueden mezclar y combinar fácilmente a las necesidades de un sistema objetivo particular. Determinar qué recursos pertenecen a qué paquete de implementación generalmente implica algunas consideraciones específicas de diseño físico que no tienen nada que ver directamente con el diseño lógico. Por ejemplo, es posible que desee intercambiar ciertas bibliotecas de procesamiento de archivos de imagen dependiendo de qué cliente recibirá un conjunto determinado de objetos de implementación.

En la práctica, encontrará que un buen diseño lógico generalmente conduce a un buen diseño físico.

En resumen, el principio más importante es el embalaje inteligente. Al organizar sus materiales de código en bibliotecas fuertemente relacionadas, termina con útiles artefactos de código desplegables que puede reutilizar, mover o regalar fácilmente.

John Tobler
fuente