¿Por qué es malo el "acoplamiento estrecho entre funciones y datos"?

38

Encontré esta cita en " La alegría de Clojure "en la p. 32, pero alguien me dijo lo mismo durante la cena la semana pasada y también lo he oído en otros lugares:

  

[A] la desventaja de la programación orientada a objetos es la estrecha   acoplamiento entre función y datos.

Entiendo por qué el acoplamiento innecesario es malo en una aplicación. También me siento cómodo al decir que el estado mutable y la herencia deben evitarse, incluso en la programación orientada a objetos. Pero no veo por qué las funciones de pegado en las clases son intrínsecamente malas.

Quiero decir, agregar una función a una clase parece etiquetar un correo en Gmail o pegar un archivo en una carpeta. Es una técnica de organización que te ayuda a encontrarla de nuevo. Escoges algunos criterios, luego pones las cosas juntas. Antes de OOP, nuestros programas eran bastante grandes bolsas de métodos en archivos. Quiero decir, tienes que poner funciones en alguna parte. ¿Por qué no organizarlos?

Si este es un ataque velado a los tipos, ¿por qué no dicen simplemente que restringir el tipo de entrada y salida a una función es incorrecto? No estoy seguro de si podría estar de acuerdo con eso, pero al menos estoy familiarizado con los argumentos pro y estafa de seguridad. Esto me suena como una preocupación mayoritariamente separada.

Claro, a veces las personas se equivocan y ponen la funcionalidad en la clase incorrecta. Pero comparado con otros errores, esto parece ser un inconveniente muy pequeño.

Por lo tanto, Clojure tiene espacios de nombres. ¿En qué se diferencia una función de OOP en una clase en OOP de una función en un espacio de nombres en Clojure y por qué es tan malo? Recuerde, las funciones en una clase no necesariamente operan solo en los miembros de esa clase. Mire java.lang.StringBuilder: funciona con cualquier tipo de referencia, o mediante boxeo automático, con cualquier tipo.

P.S. Esta cita hace referencia a un libro que no he leído: Programación multiparadigmática en Leda: Timothy Budd, 1995 .

    
pregunta GlenPeterson 25.09.2013 - 14:52

6 respuestas

33

En teoría, el acoplamiento de datos y funciones suelto facilita agregar más funciones para trabajar con los mismos datos. La desventaja es que hace que sea más difícil cambiar la estructura de datos en sí misma, por lo que en la práctica, el código funcional bien diseñado y el código OOP bien diseñado tienen niveles de acoplamiento muy similares.

Tome un gráfico acíclico dirigido (DAG) como una estructura de datos de ejemplo. En la programación funcional, todavía necesita algo de abstracción para evitar que se repita, así que va a crear un módulo con funciones para agregar y eliminar nodos y bordes, encontrar nodos accesibles desde un nodo determinado, crear una clasificación topológica, etc. Esas funciones se acoplan de manera efectiva a los datos, aunque el compilador no los aplique. Puede agregar un nodo de la manera más difícil, pero ¿por qué querría hacerlo? La cohesión dentro de un módulo evita el acoplamiento apretado en todo el sistema.

A la inversa, en el lado de la programación orientada a objetos, cualquier otra función que no sea las operaciones básicas del DAG se realizará en clases de "vista" separadas, con el objeto DAG pasado como parámetro. Es tan fácil agregar tantas vistas como desee que operen en los datos del DAG, creando el mismo nivel de desacoplamiento de datos de función que encontraría en el programa funcional. El compilador no evitará que abarrotes todo en una clase, pero tus colegas lo harán.

Cambiar los paradigmas de programación no cambia las mejores prácticas de abstracción, cohesión y acoplamiento, solo cambia las prácticas que el compilador le ayuda a aplicar. En la programación funcional, cuando se desea un acoplamiento de datos de función, se hace cumplir por acuerdo de los caballeros en lugar del compilador. En la POO, la separación del modelo de vista se aplica mediante un acuerdo entre caballeros y no por el compilador.

    
respondido por el Karl Bielefeldt 25.09.2013 - 16:49
13

En caso de que no lo supiera, tome esta información: Los conceptos de orientados a objetos y cierres son dos caras de la misma moneda. Dicho esto, ¿qué es un cierre? Toma variables o datos del ámbito circundante y se enlaza con ella dentro de la función, o desde una perspectiva OO, hace lo mismo cuando, por ejemplo, pasa algo a un constructor para que luego pueda usarlo. pieza de datos en una función miembro de esa instancia. Pero tomar las cosas desde el alcance del entorno no es algo bueno, ya que cuanto mayor sea el alcance del entorno, más mal es hacerlo (aunque de manera pragmática, a menudo es necesario algo del mal para hacer el trabajo). El uso de variables globales está llevando esto al extremo, donde las funciones de un programa están usando variables en el alcance del programa, lo que es realmente malo. Hay buenas descripciones sobre por qué las variables globales son malas.

Si sigues las técnicas de OO, básicamente ya aceptas que cada módulo de tu programa tendrá un nivel mínimo de maldad. Si adopta un enfoque funcional de la programación, apunta a un ideal donde ningún módulo de su programa contenga el mal de cierre, aunque es posible que tenga algunos, pero será mucho menos que OO.

Esa es la desventaja de OO: fomenta este tipo de mal, el acoplamiento de datos para funcionar a través de hacer cierres estándar (un tipo de Teoría de la ventana rota de la programación).

El único aspecto positivo es que, para empezar, si sabía que iba a utilizar muchos cierres, al menos OO le brinda un marco idealógico para ayudarlo a organizar ese enfoque de modo que el programador promedio pueda entenderlo. En particular, las variables que se cierran son explícitas en el constructor, en lugar de simplemente tomadas implícitamente en el cierre de una función. Los programas funcionales que usan muchos cierres suelen ser más crípticos que el programa OO equivalente, aunque no necesariamente menos elegantes :)

    
respondido por el Benedict 25.09.2013 - 16:56
7

Se trata de tipo de acoplamiento:

Una función integrada en un objeto para trabajar en ese objeto no se puede usar en otros tipos de objetos.

En Haskell, usted escribe funciones para trabajar contra el tipo clases , por lo que hay muchos tipos diferentes de objetos contra los que cualquier función puede funcionar, siempre que sea un tipo de la clase dada esa función funciona.

Las funciones independientes permiten el desacoplamiento que no obtiene cuando se enfoca en escribir sus funciones para que funcionen dentro del tipo A porque entonces no puede usarlas si no tiene una instancia del tipo A, aunque de lo contrario, la función podría ser lo suficientemente general para ser utilizada en una instancia de tipo B o en una instancia de tipo C.

    
respondido por el Jimmy Hoffa 25.09.2013 - 17:48
4

En Java y en encarnaciones similares de OOP, los métodos de instancia (a diferencia de las funciones gratuitas o los métodos de extensión) no se pueden agregar desde otros módulos.

Esto se convierte más en una restricción cuando se consideran interfaces que solo pueden implementarse mediante los métodos de instancia. No puede definir una interfaz y una clase en diferentes módulos y luego usar el código de un tercer módulo para unirlos. Un enfoque más flexible, como las clases de tipos de Haskell, debería poder hacer eso.

    
respondido por el CodesInChaos 25.09.2013 - 15:26
3

La orientación a objetos se basa fundamentalmente en la abstracción de datos de procedimiento (o la abstracción de datos funcionales si elimina efectos secundarios que son un problema ortogonal). En cierto sentido, Lambda Cálculo es el lenguaje más antiguo y puro orientado a objetos, ya que solo proporciona la abstracción de datos funcionales (porque no tiene ninguna construcción aparte de las funciones).

Solo las operaciones de un objeto único pueden inspeccionar la representación de datos de ese objeto. Ni siquiera otros objetos del mismo tipo pueden hacer eso. (Esta es la principal diferencia entre la abstracción de datos orientada a objetos y los tipos de datos abstractos: con los ADT, los objetos del mismo tipo pueden inspeccionar la representación de datos de cada uno, solo se oculta la representación de objetos de otros . )

Lo que esto significa es que varios objetos del mismo tipo pueden tener diferentes representaciones de datos. Incluso el mismo objeto puede tener diferentes representaciones de datos en diferentes momentos. (Por ejemplo, en Scala, Map s y Set s cambian entre una matriz y un hash trie dependiendo del número de elementos porque para números muy pequeños, la búsqueda lineal en una matriz es más rápida que la búsqueda logarítmica en un árbol de búsqueda porque de los factores constantes muy pequeños.)

Desde el exterior de un objeto, no debería, no puede conocer su representación de datos. Ese es el opuesto del acoplamiento apretado.

    
respondido por el Jörg W Mittag 25.09.2013 - 16:07
1

El acoplamiento estrecho entre los datos y las funciones es malo porque quiere poder cambiar cada uno independientemente del otro y el acoplamiento apretado lo dificulta porque no puede cambiar uno sin conocer y posiblemente cambiar al otro.

Desea que se presenten diferentes datos a la función para que no requieran ningún cambio en la función y, de manera similar, desea poder realizar cambios en la función sin que sea necesario realizar ningún cambio en los datos en los que está operando para admitir dichos cambios.

    
respondido por el Michael Durrant 25.09.2013 - 17:31

Lea otras preguntas en las etiquetas