Estoy tratando de aprender GRASP y encontré esto explicado ( aquí en la página 3 ) sobre Acoplamiento bajo y me sorprendió mucho cuando encontré esto:
Considere el método
addTrack
para unaAlbum
clase, dos métodos posibles son:
addTrack( Track t )
y
addTrack( int no, String title, double duration )
¿Qué método reduce el acoplamiento? El segundo sí, ya que la clase que usa la clase Álbum no tiene que conocer una clase Track. En general, los parámetros de los métodos deben usar tipos base (int, char ...) y clases de los paquetes java. *.
Tiendo a estar de acuerdo con esto; Creo que addTrack(Track t)
es mejor que addTrack(int no, String title, double duration)
por varias razones:
Siempre es mejor que un método tenga la menor cantidad de parámetros posible (de acuerdo con el Código de limpieza del tío Bob, ninguno o uno preferiblemente, 2 en algunos casos y 3 en casos especiales; más de 3 necesitan refactorización; estas son, por supuesto, recomendaciones, no reglas de acebo) .
Si se
addTrack
trata de un método de una interfaz, y los requisitos necesitan queTrack
se tenga más información (por ejemplo, año o género), entonces la interfaz debe cambiarse y el método debe admitir otro parámetro.La encapsulación está rota; si
addTrack
está en una interfaz, entonces no debe conocer los elementos internos deTrack
.En realidad, está más acoplado en la segunda forma, con muchos parámetros. Supongamos que el
no
parámetro necesita ser cambiado deint
along
porque hay más deMAX_INT
pistas (o por cualquier razón); entonces, tanto elTrack
método como el método deben cambiarse, mientras que si el método fueraaddTrack(Track track)
solo elTrack
, se cambiaría.
Los 4 argumentos están realmente conectados entre sí, y algunos de ellos son consecuencias de otros.
¿Qué enfoque es mejor?
Respuestas:
Bueno, sus primeros tres puntos son en realidad sobre otros principios además del acoplamiento. Siempre tiene que encontrar un equilibrio entre los principios de diseño a menudo conflictivos.
Su cuarto punto es sobre el acoplamiento, y estoy totalmente de acuerdo con usted. El acoplamiento se trata del flujo de datos entre módulos. El tipo de contenedor en el que fluyen los datos es en gran medida irrelevante. Pasar una duración como un doble en lugar de como un campo de a
Track
no elimina la necesidad de pasarlo. Los módulos aún necesitan compartir la misma cantidad de datos y aún tienen la misma cantidad de acoplamiento.Tampoco está considerando todo el acoplamiento en el sistema como un agregado. Si bien la introducción de una
Track
clase ciertamente agrega otra dependencia entre dos módulos individuales, puede reducir significativamente el acoplamiento del sistema , que es la medida importante aquí.Por ejemplo, considere un botón "Agregar a lista de reproducción" y un
Playlist
objeto. SeTrack
podría considerar la introducción de un objeto para aumentar el acoplamiento si solo considera esos dos objetos. Ahora tiene tres clases interdependientes en lugar de dos. Sin embargo, esa no es la totalidad de su sistema. También debe importar la pista, reproducir la pista, mostrar la pista, etc. Agregar una clase más a esa mezcla es insignificante.Ahora considere la necesidad de agregar soporte para reproducir pistas a través de la red en lugar de solo localmente. Solo necesita crear un
NetworkTrack
objeto que se ajuste a la misma interfaz. Sin elTrack
objeto, tendría que crear funciones en todas partes como:Eso efectivamente duplica su acoplamiento, lo que requiere que incluso los módulos que no se preocupan por las cosas específicas de la red aún lo sigan, para poder transmitirlo.
Su prueba de efecto dominó es buena para determinar su verdadera cantidad de acoplamiento. Lo que nos preocupa es limitar los lugares a los que afecta un cambio.
fuente
Mi recomendación es:
Utilizar
pero asegúrese de que
ITrack
sea una interfaz y no una clase concreta.Album no conoce los aspectos internos de los
ITrack
implementadores. Solo está acoplado al contrato definido por elITrack
.Creo que esta es la solución que genera la menor cantidad de acoplamiento.
fuente
Track
ser tonto o inteligente.Track
Es una concreción.ITrack
La interfaz es una abstracción. De esa manera, podrá tener diferentes tipos de pistas en el futuro, siempre que cumplan con ellasITrack
.Yo diría que el segundo método de ejemplo probablemente aumenta el acoplamiento, ya que lo más probable es crear instancias de un objeto Track y almacenarlo en el objeto Album actual. (Como se sugirió en mi comentario anterior, asumiría que es inherente que una clase de Álbum tenga el concepto de una clase de Pista en algún lugar dentro de ella).
El primer método de ejemplo supone que se está instanciando una Pista fuera de la clase Álbum, por lo que, como mínimo, podemos suponer que la instanciación de la clase Pista no está acoplada a la clase Álbum.
Si las mejores prácticas sugirieran que nunca tenemos una referencia de clase en una segunda clase, la totalidad de la programación orientada a objetos se descartaría.
fuente
El acoplamiento es solo uno de los muchos aspectos a tratar de obtener en su código. Al reducir el acoplamiento, no necesariamente está mejorando su programa. En general, esta es una mejor práctica, pero en este caso particular, ¿por qué no debería
Track
debería conocerse?Al usar una
Track
clase a la que pasarAlbum
, está haciendo que su código sea más fácil de leer, pero lo más importante, como mencionó, está convirtiendo una lista estática de parámetros en un objeto dinámico. Eso finalmente hace que su interfaz sea mucho más dinámica.Usted menciona que la encapsulación está rota, pero no lo está.
Album
debe conocer los aspectos internos deTrack
, y si no usó un objeto,Album
tendría que conocer todos y cada uno de los datos que se le pasan antes de que pueda utilizarlo de todos modos. La persona que llama también debe conocer los aspectos internos deTrack
, ya que debe construir unTrack
objeto, pero la persona que llama debe conocer esta información de todos modos si se pasa directamente al método. En otras palabras, si la ventaja de la encapsulación es no conocer el contenido de un objeto, no podría utilizarse en este caso, ya queAlbum
debe hacer uso deTrack
la información de la misma manera.Donde no querrá usar
Track
es siTrack
contiene lógica interna a la que no le gustaría que la persona que llama tenga acceso. En otras palabras, siAlbum
fuera una clase que un programador que usa tu biblioteca usaría, no querrías que él useTrack
si la usa para decir, llame a un método para mantenerla en la base de datos. El verdadero problema con esto radica en el hecho de que la interfaz está enredada con el modelo.Para solucionar el problema, necesitaría separarse
Track
en sus componentes de interfaz y sus componentes lógicos, creando dos clases separadas. Para la persona que llama, seTrack
convierte en una clase ligera que está destinada a contener información y ofrecer optimizaciones menores (datos calculados y / o valores predeterminados). En el interiorAlbum
, usaría una clase nombradaTrackDAO
para realizar el trabajo pesado asociado con guardar la información deTrack
la base de datos.Por supuesto, esto es solo un ejemplo. Estoy seguro de que este no es su caso en absoluto, así que siéntase libre de usar sin
Track
culpa. Solo recuerde tener en cuenta a la persona que llama cuando construye clases y crear interfaces cuando sea necesario.fuente
Ambos son correctos
es mejor (como ya discutiste) mientras
está menos acoplado porque el código que usa
addTrack
no necesita saber que hay unaTrack
clase. Se puede cambiar el nombre de la pista, por ejemplo, sin la necesidad de actualizar el código de llamada.Mientras habla de un código más legible / mantenible, el artículo habla de acoplamiento . Un código menos acoplado no es necesariamente más fácil de implementar y comprender.
fuente
Acoplamiento bajo no significa sin acoplamiento. Algo, en algún lugar, debe saber acerca de los objetos en otras partes de la base de código, y cuanto más reduzca la dependencia de los objetos "personalizados", más razones dará para que cambie el código. Lo que el autor que cita está promoviendo con la segunda función está menos acoplado, pero también menos orientado a objetos, lo que es contrario a la idea de que GRASP sea una metodología de diseño orientada a objetos . El punto es cómo diseñar el sistema como una colección de objetos y sus interacciones; evitarlos es como enseñarle a conducir un automóvil diciéndole que debe andar en bicicleta.
En cambio, la vía adecuada es reducir la dependencia de los objetos concretos , que es la teoría del "acoplamiento flojo". Cuantos menos tipos concretos concretos tenga que conocer un método, mejor. Solo con esa declaración, la primera opción está en realidad menos acoplada, porque el segundo método que toma los tipos más simples debe conocer todos esos tipos más simples. Seguro que están incorporados, y el código dentro del método puede tener que importar, pero la firma del método y las personas que llaman definitivamente no lo hacen . Cambiar uno de estos parámetros relacionados con una pista de audio conceptual requerirá más cambios cuando están separados versus cuando están contenidos en un objeto de Pista (que es el punto de los objetos; encapsulación).
Yendo un paso más allá, si se espera que Track sea reemplazado por algo que hizo el mismo trabajo mejor, tal vez una interfaz que defina la funcionalidad requerida estaría en orden, un ITrack. Eso podría permitir implementaciones diferentes, como "AnalogTrack", "CdTrack" y "Mp3Track", que proporcionaban información adicional más específica para esos formatos, al tiempo que proporcionaban la exposición de datos básicos de ITrack que conceptualmente representa una "pista"; Una sub-pieza finita de audio. Track también podría ser una clase base abstracta, pero esto requiere que siempre desee utilizar la implementación inherente en Track; vuelva a implementarlo como BetterTrack y ahora debe cambiar los parámetros esperados.
Así la regla de oro; Los programas y sus componentes de código siempre tendrán motivos para cambiar. No puede escribir un programa que nunca requiera editar el código que ya ha escrito para agregar algo nuevo o modificar su comportamiento. Su objetivo, en cualquier metodología (GRASP, SOLID, cualquier otro acrónimo o palabra de moda que pueda imaginar) es simplemente identificar las cosas que tendrán que cambiar con el tiempo y diseñar el sistema para que esos cambios sean lo más fáciles de hacer posible (traducido; tocar la menor cantidad de líneas de código y afectar la menor cantidad posible de otras áreas del sistema más allá del alcance de su cambio previsto). Caso en cuestión, lo que es más probable que cambie es que un Track obtendrá más miembros de datos que a addTrack () le pueden importar o no, no esa pista se reemplazará con BetterTrack.
fuente