Pregunta

El acoplamiento suelto es, para algunos desarrolladores, el santo grial del software bien diseñado. Ciertamente, es bueno cuando hace que el código sea más flexible frente a los cambios que probablemente ocurran en el futuro previsible o evita la duplicación del código.

Por otro lado, los esfuerzos para acoplar componentes libremente aumentan la cantidad de indirección en un programa, lo que aumenta su complejidad, lo que a menudo hace que sea más difícil de entender y, a menudo, lo hace menos eficiente.

¿Considera un enfoque en el acoplamiento suelto sin ningún caso de uso para el acoplamiento suelto (como evitar la duplicación del código o la planificación de cambios que probablemente ocurran en el futuro previsible) como un antipatrón? ¿Puede el acoplamiento suelto caer bajo el paraguas de Yagni?

¿Fue útil?

Solución

Un poco.

A veces, el acoplamiento suelto que viene sin demasiado esfuerzo está bien, incluso si no tiene requisitos específicos que exigan que se desacople algún módulo. La fruta baja, por así decirlo.

Por otro lado, la generación excesiva para cantidades ridículas de cambios será una gran complejidad y esfuerzo innecesarios. Yagni, como dices, lo golpea en la cabeza.

Otros consejos

Es práctica de programación X ¿bueno o malo? Claramente, la respuesta es siempre "depende."

Si está mirando su código, preguntándose qué "patrones" puede inyectar, entonces lo está haciendo mal.

Si está construyendo su software para que los objetos no relacionados no jugueten entre sí, entonces lo están haciendo bien.

Si está "ingeniería" su solución para que pueda extenderse y cambiar infinitamente, entonces en realidad la está haciendo más complicado.

Creo que al final del día, te queda la verdad única: ¿es más o menos complicado tener los objetos desacoplados? Si es menos complicado acoplarlos, esa es la solución correcta. Si es menos complicado desacoplarlos, entonces esa es la solución correcta.

(Actualmente estoy trabajando en una base de código bastante pequeña que hace un trabajo simple de una manera muy complicada, y parte de lo que lo hace tan complicado es la falta de comprensión de los términos "acoplamiento" y "cohesión" por parte del original desarrolladores.)

Creo que a lo que estás haciendo aquí es el concepto de cohesión. ¿Este código tiene un buen propósito? ¿Puedo internalizar ese propósito y comprender el "panorama general" de lo que está pasando?

Podría conducir a un código difícil de seguir, no solo porque hay muchos más archivos fuente (suponiendo que estas son clases separadas), sino porque ninguna clase única parece tener un propósito.

Desde una perspectiva ágil, podría sugerir que tal acoplamiento suelto sería un antipatrón. Sin la cohesión, o incluso los casos de uso, no puede escribir pruebas unitarias sensibles y no puede verificar el propósito del código. Ahora, el código ágil puede conducir a un acoplamiento suelto, por ejemplo, cuando se usa el desarrollo basado en la prueba. Pero si se crearon las pruebas correctas, en el orden correcto, entonces es probable que haya buena cohesión y acoplamiento suelto. Y estás hablando solo de los casos para cuando claramente no hay cohesión.

Nuevamente desde la perspectiva ágil, no desea este nivel artificial de indirección porque se desperdicia esfuerzo en algo que probablemente no se necesitará de todos modos. Es mucho más fácil refactorizar cuando la necesidad es real.

En general, desea un alto acoplamiento dentro de sus módulos y un acoplamiento suelto entre ellos. Sin el alto acoplamiento, probablemente no tenga cohesión.

Para la mayoría de las preguntas como esta, la respuesta es "depende". En general, si puedo hacer un diseño lógicamente acoplado de manera que tenga sentido, sin una sobrecarga importante para hacerlo, lo haré. Evitar el acoplamiento innecesario en el código es, en mi opinión, un objetivo de diseño completamente valioso.

Una vez que llegue a una situación en la que parece que los componentes deberían estar lógicamente estrechamente acoplados, buscaré un argumento convincente antes de comenzar a romperlos.

Supongo que el principio al que trabajo con la mayoría de estos tipos de práctica es de inercia. Tengo una idea de cómo me gustaría que funcionara mi código y si puedo hacerlo de esa manera sin hacer la vida más difícil, lo haré. Si lo hará, el desarrollo más difícil, pero el mantenimiento y el trabajo futuro sean más fáciles, intentaré encontrar una suposición de si será más trabajo en toda la vida del código y lo usará como mi guía. De lo contrario, debería ser un punto de diseño deliberado para valer la pena.

La respuesta simple es el acoplamiento suelto es bueno cuando se hace correctamente.

Si el principio de una función, se sigue un propósito, entonces debería ser bastante fácil seguir lo que está sucediendo. Además, el código de pareja suelta sigue como una cuestión de por supuesto sin ningún esfuerzo.

Reglas de diseño simple: 1. No cree el conocimiento de múltiples elementos en un solo punto (como se señala en todas partes, depende) a menos que esté construyendo una interfaz de fachada. 2. Una función, un propósito (ese propósito puede ser multifacético, como en una fachada) 3. Un módulo, un conjunto claro de funciones interrelacionadas, un propósito claro 4. Si no puede simplemente probarlo, entonces no tiene Un simple propósito

Todos estos comentarios sobre más fáciles de refactorizar más tarde son una carga de bacalaes. Una vez que el conocimiento se incorpora a muchos lugares, particularmente en los sistemas distribuidos, el costo de la refactorización, la sincronización de despliegue y casi cualquier otro costo lo explota tanto que en la mayoría de los casos el sistema termina siendo destrozado por él.

Lo triste del desarrollo de software en estos días es que el 90% de las personas desarrollan nuevos sistemas y no tienen capacidad para comprender los sistemas antiguos, y nunca están presentes cuando el sistema tiene un estado de salud tan pobre debido a la continua refactorización de bits y piezas.

No importa cuán apretado, una cosa es para otra si esa otra cosa nunca cambia. En general, me ha encontrado más productivo a lo largo de los años centrarse en buscar menos razones para que las cosas cambien, buscar estabilidad, que hacerlas más fáciles de cambiar tratando de lograr la forma más floja de acoplamiento posible.Desacoplamiento He descubierto que es muy útil, hasta el punto en que a veces favorezco la duplicación de código modesto a los paquetes decouple. Como ejemplo básico, tenía la opción de usar mi biblioteca de matemáticas para implementar una biblioteca de imágenes. No hice y solo dupliqué algunas funciones matemáticas básicas que eran triviales de copiar.

Ahora mi biblioteca de imágenes es completamente independiente de la biblioteca de matemáticas de una manera donde no importa qué tipo de cambios haga a mi Lib Math, no afectará la biblioteca de imágenes. Eso es poner estabilidad en primer lugar. La biblioteca de imágenes es más estable ahora, ya que al tener drásticamente menos razones para cambiar, ya que se desacopla desde cualquier otra biblioteca que pueda cambiar (además de la biblioteca estándar C que, con suerte, nunca debería cambiar). Como beneficio adicional, también es fácil de implementar cuando es solo una biblioteca independiente que no requiere atraer un montón de otras libs para construirlo y usarlo.

La estabilidad es muy útil para mí. Me gusta construir una colección de código bien probado que tenga cada vez menos razones para cambiar en el futuro. Eso no es un sueño imposible; Tengo el código C que he estado usando y usando nuevamente desde finales de los 80, lo que no ha cambiado desde entonces. Es ciertamente cosas de bajo nivel como el código orientado a píxeles y relacionados con la geometría, mientras que muchas de mis cosas de nivel superior se volvieron obsoletas, pero es algo que todavía ayuda mucho a tener. Eso casi siempre significa una biblioteca que se basa en cada vez menos cosas, por nada externo. La fiabilidad aumenta y aumenta si su software depende cada vez más de bases estables que encuentren pocas o ninguna razón para cambiar. Menos piezas móviles son realmente agradables, incluso si en la práctica las partes móviles son mucho mayores que las partes estables. Todavía ayuda mucho a mi cordura saber que la totalidad de una base de código no consiste en partes móviles.

El acoplamiento suelto está en la misma línea, pero a menudo encuentro que el acoplamiento suelto es mucho menos estable que no un acoplamiento. A menos que esté trabajando en un equipo con diseñadores de interfaz y clientes muy superiores que no cambian de opinión de lo que nunca trabajé, incluso las interfaces puras a menudo encuentran razones para cambiar de manera que aún causa roturas en cascada a lo largo del código. Esta idea de que la estabilidad se puede lograr dirigiendo las dependencias hacia el abstracto en lugar de lo concreto solo es útil si el diseño de la interfaz es más fácil de obtener la primera vez que la implementación. A menudo me parece que se invierte cuando un desarrollador podría haber creado una implementación muy buena, si no maravillosa, dados los requisitos de diseño que pensaban que deberían cumplir, solo para encontrar en el futuro que los requisitos de diseño cambian por completo. El diseño es mucho más difícil de obtener bien que la implementación si me pregunta con bases de código a gran escala que intentan cumplir con los requisitos en constante cambio, y en ese caso separar los diseños abstractos de implementaciones concretas no ayuda tanto si el diseño abstracto es lo que más es propenso a ser cambiado.

Por lo tanto, me gusta favorecer la estabilidad y el desacoplamiento completo para que al menos pueda decir con confianza: "Esta pequeña biblioteca aislada que se ha utilizado durante años y asegurada mediante pruebas exhaustivas casi no tiene probabilidad de requerir cambios sin importar lo que sucede en el mundo caótico fuera del mundo " Me da una pequeña porción de cordura sin importar qué tipo de cambio de diseño se requiera para afuera.

Acoplamiento y estabilidad, ejemplo de ECS

También me encantan los sistemas de componentes de entidad e introducen un montón de acoplamiento estricto porque el sistema a las dependencias de componentes accede y manipulan los datos sin procesar directamente, así como así:

enter image description here

Todas las dependencias aquí son bastante apretadas ya que los componentes solo exponen datos sin procesar. Las dependencias no fluyen hacia abstracciones, están fluyendo hacia datos sin procesar lo que significa que cada sistema tiene la máxima cantidad de conocimiento posible sobre cada tipo de componente al que solicitan acceder. Los componentes no tienen funcionalidad con todos los sistemas que acceden y manipulan los datos sin procesar. Sin embargo, es muy fácil razonar sobre un sistema como este, ya que es muy plano. Si una textura sale atornillada, entonces sabe con este sistema inmediatamente que solo el sistema de renderización y pintura accede a los componentes de textura, y probablemente pueda descartar rápidamente el sistema de renderizado, ya que solo se lee de las texturas conceptualmente.

Mientras tanto, una alternativa poco acoplada podría ser esta:

enter image description here

... Con todas las dependencias que fluyen hacia funciones abstractas, no datos, y cada cosa en ese diagrama que expone una interfaz pública y una funcionalidad propia. Aquí todas las dependencias pueden estar muy flojas. Es posible que los objetos ni siquiera dependan directamente entre sí e interactúen entre sí a través de interfaces puras. Aún así, es muy difícil razonar sobre este sistema, especialmente si algo sale mal, dada la compleja maraña de interacciones. También habrá más interacciones (más acoplamiento, aunque más flexible) que el ECS porque las entidades tienen que saber sobre los componentes que agregan, incluso si solo conocen la interfaz pública abstracta de los demás.

Además, si hay cambios de diseño en algo, obtiene más roturas en cascada que el ECS, y generalmente habrá más razones y tentaciones para los cambios de diseño, ya que cada cosa está tratando de proporcionar una buena interfaz y abstracción orientadas a objetos. Eso inmediatamente viene con la idea de que cada pequeña cosa tratará de imponer restricciones y limitaciones al diseño, y esas restricciones son a menudo lo que el diseño cambia. La funcionalidad está mucho más limitada y tiene que hacer tantas suposiciones de diseño más que los datos sin procesar.

En la práctica, en la práctica, el tipo de sistema ECS "plano" anterior es mucho más fácil de razonar que incluso los sistemas más poco acoplados con una compleja telaraña de dependencias sueltas y, lo más importante, para mí, encuentro tan pocas razones. Para que la versión ECS necesite cambiar los componentes existentes, ya que los componentes dependían de no tener responsabilidad, excepto proporcionar los datos apropiados necesarios para que los sistemas funcionen. Compare la dificultad de diseñar un puro IMotion La interfaz y el objeto de movimiento concreto implementan esa interfaz que proporciona una funcionalidad sofisticada mientras intenta mantener invariantes sobre datos privados frente a un componente de movimiento que solo necesita proporcionar datos sin procesar relevantes para resolver el problema y no se molesta con la funcionalidad.

La funcionalidad es mucho más difícil de obtener bien que los datos, por lo que creo que a menudo es preferible dirigir el flujo de dependencias hacia los datos. Después de todo, ¿cuántas bibliotecas vectoriales/matriciales existen? ¿Cuántos de ellos usan exactamente la misma representación de datos y solo difieren sutilmente en la funcionalidad? Innumerables, y sin embargo, todavía tenemos tantos a pesar de las representaciones de datos idénticas porque queremos diferencias sutiles en la funcionalidad. ¿Cuántas bibliotecas de imágenes existen? ¿Cuántos de ellos representan píxeles de una manera diferente y única? Apenas ninguno, y nuevamente muestra que la funcionalidad es mucho más inestable y propensa a los cambios de diseño que los datos en muchos escenarios. Por supuesto, en algún momento necesitamos funcionalidad, pero puede diseñar sistemas donde la mayor parte de las dependencias fluya hacia los datos, y no hacia las abstracciones o la funcionalidad en general. Tal sería priorizar la estabilidad por encima del acoplamiento.

Las funciones más estables que he escrito (del tipo que he estado usando y reutilizando desde finales de los 80 sin tener que cambiarlas) fueron todas las que dependían de datos sin procesar, como una función de geometría que acaba de aceptar una variedad de una variedad de una variedad de flotadores e enteros, no los que dependen de un complejo Mesh objeto o IMesh interfaz o multiplicación vectorial/matriz que solo dependía de float[] o double[], no uno que dependiera de FancyMatrixObjectWhichWillRequireDesignChangesNextYearAndDeprecateWhatWeUse.

Licenciado bajo: CC-BY-SA con atribución
scroll top