oop - loosely - ¿Qué es "acoplamiento flojo"? Proporcione ejemplos
loosely coupled (21)
Definición
Básicamente, el acoplamiento es la cantidad en que un objeto determinado o un conjunto de objetos depende de otro objeto u otro conjunto de objetos para cumplir su tarea.
Alto acoplamiento
Piensa en un auto. Para que el motor arranque, se debe insertar una llave en el encendido, se debe girar, la gasolina debe estar presente, debe producirse una chispa, los pistones deben dispararse y el motor debe encenderse. Se podría decir que el motor de un automóvil está muy acoplado a muchos otros objetos. Este es un alto acoplamiento, pero en realidad no es algo malo.
Bajo acoplamiento
Piense en un control de usuario para una página web que es responsable de permitir a los usuarios publicar, editar y ver algún tipo de información. El control único se puede usar para permitir que un usuario publique una nueva información o edite una nueva información. El control debería poder compartirse entre dos rutas diferentes: nueva y editar. Si el control está escrito de tal manera que necesita algún tipo de datos de las páginas que lo contendrán, entonces podría decir que está muy acoplado. El control no debería necesitar nada de su página de contenedor.
No puedo asimilar el concepto de "acoplamiento libre". Supongo que no ayuda que la palabra "suelta" generalmente tenga una connotación negativa, así que siempre olvido que el acoplamiento flojo es algo bueno .
¿Alguien mostrará algún código "antes" y "después" (o pseudocódigo) que ilustre este concepto?
Algunas respuestas largas aquí. El principio es muy simple sin embargo. Presento la declaración de apertura de wikipedia :
"Loose coupling describe una relación flexible entre dos o más sistemas u organizaciones con algún tipo de relación de intercambio.
Cada final de la transacción hace que sus requisitos sean explícitos y hace pocas suposiciones sobre el otro extremo ".
Considere una aplicación de Windows con FormA y FormB. FormA es la forma primaria y muestra FormB. Imagine que el FormB necesita pasar datos a su padre.
Si hiciste esto:
class FormA
{
FormB fb = new FormB( this );
...
fb.Show();
}
class FormB
{
FormA parent;
public FormB( FormA parent )
{
this.parent = parent;
}
}
FormB está estrechamente acoplado a FormA. FormB no puede tener otro padre que el de tipo FormA.
Si, por otro lado, usted tenía FormB publicar un evento y tener FormA suscribirse a ese evento, entonces FormB podría enviar datos a través de ese evento al suscriptor que tenga ese evento. En este caso, FormB ni siquiera sabe que está respondiendo a su padre; a través del acoplamiento flexible, el evento proporciona que simplemente está hablando con los suscriptores. Cualquier tipo ahora puede ser un padre para FormA.
rp
Considere una simple aplicación de carrito de compras que utiliza una clase CartContents para realizar un seguimiento de los artículos en el carrito de compras y una clase de pedido para procesar una compra. El pedido necesita determinar el valor total de los contenidos en el carro, podría hacerlo así:
Muy bien acoplado ejemplo:
public class CartEntry
{
public float Price;
public int Quantity;
}
public class CartContents
{
public CartEntry[] items;
}
public class Order
{
private CartContents cart;
private float salesTax;
public Order(CartContents cart, float salesTax)
{
this.cart = cart;
this.salesTax = salesTax;
}
public float OrderTotal()
{
float cartTotal = 0;
for (int i = 0; i < cart.items.Length; i++)
{
cartTotal += cart.items[i].Price * cart.items[i].Quantity;
}
cartTotal += cartTotal*salesTax;
return cartTotal;
}
}
Observe cómo el método OrderTotal (y, por lo tanto, la clase Order) depende de los detalles de implementación de las clases CartContents y CartEntry. Si intentáramos cambiar esta lógica para permitir descuentos, probablemente tendríamos que cambiar las 3 clases. Además, si cambiamos a usar una colección de Lista para hacer un seguimiento de los artículos, también tendríamos que cambiar la clase de Orden.
Ahora aquí hay una manera un poco mejor de hacer lo mismo:
Ejemplo menos acoplado:
public class CartEntry
{
public float Price;
public int Quantity;
public float GetLineItemTotal()
{
return Price * Quantity;
}
}
public class CartContents
{
public CartEntry[] items;
public float GetCartItemsTotal()
{
float cartTotal = 0;
foreach (CartEntry item in items)
{
cartTotal += item.GetLineItemTotal();
}
return cartTotal;
}
}
public class Order
{
private CartContents cart;
private float salesTax;
public Order(CartContents cart, float salesTax)
{
this.cart = cart;
this.salesTax = salesTax;
}
public float OrderTotal()
{
return cart.GetCartItemsTotal() * (1.0f + salesTax);
}
}
La lógica que es específica para la implementación de la línea de pedido del carro o de la colección del carrito o del pedido está restringida solo a esa clase. Entonces podríamos cambiar la implementación de cualquiera de estas clases sin tener que cambiar las otras clases. Podríamos llevar este desacoplamiento aún más mejorando el diseño, introduciendo interfaces, etc., pero creo que ve el punto.
Cuando creas un objeto de una clase usando una palabra clave new
en alguna otra clase, en realidad estás haciendo un acoplamiento ajustado (mala práctica), en su lugar debes usar un acoplamiento flexible, lo cual es una buena práctica.
--- A.java ---
package interface_package.loose_coupling;
public class A {
void display(InterfaceClass obji)
{
obji.display();
System.out.println(obji.getVar());
}
}
--- B.java ---
package interface_package.loose_coupling;
public class B implements InterfaceClass{
private String var="variable Interface";
public String getVar() {
return var;
}
public void setVar(String var) {
this.var = var;
}
@Override
public void display() {
// TODO Auto-generated method stub
System.out.println("Display Method Called");
}
}
--- InterfaceClass ---
package interface_package.loose_coupling;
public interface InterfaceClass {
void display();
String getVar();
}
---Clase principal---
package interface_package.loose_coupling;
public class MainClass {
public static void main(String[] args) {
// TODO Auto-generated method stub
A obja=new A();
B objb=new B();
obja.display(objb); //Calling display of A class with object of B class
}
}
Explicación:
En el ejemplo anterior, tenemos dos clases A y B
La clase B implementa la interfaz, es decir, InterfaceClass.
InterfaceClass define un Contrato para clase B ya que InterfaceClass tiene métodos abstractos de clase B a los que se puede acceder por cualquier otra clase, por ejemplo A.
En la clase A tenemos un método de visualización que puede exceptuar el objeto de clase que implementa InterfaceClass (en nuestro caso es clase B). Y en ese método de objeto de clase A está llamando a display () y getVar () de clase B
En MainClass hemos creado un objeto de Clase A y B. Y llamando al método de visualización de A pasando el objeto de la clase B, es decir, objb. El método de visualización de A se invocará con el objeto de clase B.
Ahora hablando de acoplamiento flojo. Supongamos que en el futuro tiene que cambiar el nombre de la Clase B a ABC, entonces no tiene que cambiar su nombre en el método de visualización de la clase B, simplemente haga el objeto de la clase nueva (ABC) y páselo al método de visualización en MailClass. No tiene que cambiar nada en la Clase A
ref: http://p3lang.com/2013/06/loose-coupling-example-using-interface/
Dos componentes están altamente acoplados cuando dependen de la implementación concreta del otro.
Supongamos que tengo este código en algún lugar de un método en mi clase:
this.some_object = new SomeObject();
Ahora mi clase depende de SomeObject, y están altamente acoplados. Por otro lado, digamos que tengo un método InjectSomeObject:
void InjectSomeObject(ISomeObject so) { // note we require an interface, not concrete implementation
this.some_object = so;
}
Entonces, el primer ejemplo solo puede usar SomeObject inyectado. Esto es útil durante la prueba. Con el funcionamiento normal, puede utilizar clases pesadas de uso de bases de datos, uso de redes, etc., mientras que las pruebas pasan una implementación liviana y simulada. Con un código estrechamente acoplado no puedes hacer eso.
Puede facilitar algunas partes de este trabajo utilizando contenedores de inyección de dependencia. Puede leer más sobre DI en Wikipedia: http://en.wikipedia.org/wiki/Dependency_injection .
A veces es fácil llevar esto demasiado lejos. En algún momento debe hacer las cosas concretas, o su programa será menos legible y comprensible. Entonces use estas técnicas principalmente en el límite de los componentes, y sepa lo que está haciendo. Asegúrese de aprovechar el acoplamiento flojo. Si no, probablemente no lo necesites en ese lugar. DI puede hacer que su programa sea más complejo. Asegúrate de hacer una buena compensación. En otras palabras, mantener un buen equilibrio. Como siempre al diseñar sistemas. ¡Buena suerte!
El acoplamiento se refiere a qué tan estrechamente diferentes clases están conectadas entre sí. Las clases estrechamente acopladas contienen una gran cantidad de interacciones y dependencias.
Las clases débilmente acopladas son lo opuesto ya que sus dependencias entre sí se mantienen al mínimo y en su lugar se basan en las interfaces públicas bien definidas entre sí.
Los legos, los juguetes que SNAP juntos se considerarían vagamente unidos porque puedes juntar las piezas y construir el sistema que desees. Sin embargo, un rompecabezas tiene piezas que están estrechamente unidas. No se puede tomar una pieza de un rompecabezas (sistema) y encajarla en un rompecabezas diferente, porque el sistema (rompecabezas) es muy dependiente de las piezas específicas que se construyeron específicamente para ese "diseño" en particular. Los legos están construidos de una manera más genérica para que puedan ser utilizados en su Lego House, o en mi Lego Alien Man.
Referencia: https://megocode3.wordpress.com/2008/02/14/coupling-and-cohesion/
El acoplamiento suelto, en general, es de 2 actores que trabajan independientemente el uno del otro en la misma carga de trabajo. Por lo tanto, si tuviera 2 servidores web que usan la misma base de datos back-end, entonces diría que esos servidores web están estrechamente acoplados. El acoplamiento ajustado se ejemplificaría teniendo 2 procesadores en un servidor web ... esos procesadores están estrechamente conectados.
Espero que sea de alguna ayuda.
El acoplamiento tiene que ver con las dependencias entre los sistemas, que podrían ser módulos de código (funciones, archivos o clases), herramientas en proceso, procesos servidor-cliente, etc. Cuanto menos generales son las dependencias, más "estrechamente conectadas" se vuelven, ya que cambiar un sistema requiere cambiar los otros sistemas que dependen de él. La situación ideal es el "acoplamiento flexible", en el que se puede cambiar un sistema y los sistemas que dependen de él continuarán funcionando sin modificaciones.
La forma general de lograr un acoplamiento flexible es a través de interfaces bien definidas. Si la interacción entre dos sistemas está bien definida y se cumple en ambos lados, entonces es más fácil modificar un sistema mientras se asegura que las convenciones no se rompan. Comúnmente ocurre en la práctica que no se establece una interfaz bien definida, lo que da como resultado un diseño descuidado y un acoplamiento ajustado.
Algunos ejemplos:
La aplicación depende de una biblioteca. En acoplamiento cerrado, la aplicación se rompe en las versiones más nuevas de la lib. Google para "DLL Hell".
La aplicación del cliente lee datos de un servidor. Bajo un acoplamiento estricto, los cambios en el servidor requieren correcciones en el lado del cliente.
Dos clases interactúan en una jerarquía orientada a objetos. Bajo un acoplamiento estricto, los cambios en una clase requieren que la otra clase se actualice para que coincida.
Varias herramientas de línea de comandos se comunican en una tubería. Si están estrechamente acoplados, los cambios en la versión de una herramienta de línea de comandos provocarán errores en las herramientas que leen su resultado.
El código estrechamente acoplado se basa en una implementación concreta. Si necesito una lista de cadenas en mi código y lo declaro así (en Java)
ArrayList<String> myList = new ArrayList<String>();
entonces dependo de la implementación de ArrayList.
Si deseo cambiar eso por un código débilmente acoplado, hago de mi referencia una interfaz (u otro tipo abstracto).
List<String> myList = new ArrayList<String>();
Esto me impide llamar a cualquier método en myList
que sea específico de la implementación de ArrayList. Estoy limitado solo a los métodos definidos en la interfaz de la Lista. Si más tarde decido que realmente necesito una LinkedList, solo necesito cambiar mi código en un lugar, donde creé la nueva lista, y no en 100 lugares donde realicé llamadas a métodos ArrayList.
Por supuesto, puede instanciar un ArrayList usando la primera declaración y evitar el uso de métodos que no forman parte de la interfaz de la Lista, pero el uso de la segunda declaración hace que el compilador lo mantenga honesto.
El grado de diferencia entre las respuestas aquí muestra por qué sería un concepto difícil de comprender, pero para decirlo tan simple como puedo describirlo:
Para que yo sepa que si te tiro una pelota, entonces puedes atraparla. Realmente no necesito saber cuántos años tienes. No necesito saber qué comiste en el desayuno, y realmente no me importa quién fue tu primer enamoramiento. Todo lo que necesito saber es que puedes atrapar. Si sé esto, entonces no me importa si te estoy tirando una pelota a ti o a tu hermano.
Con lenguajes no dinámicos como c # o Java, logramos esto a través de Interfaces. Entonces digamos que tenemos la siguiente interfaz:
public ICatcher
{
public void Catch();
}
Y ahora digamos que tenemos las siguientes clases:
public CatcherA : ICatcher
{
public void Catch()
{
console.writeline("You Caught it");
}
}
public CatcherB : ICatcher
{
public void Catch()
{
console.writeline("Your brother Caught it");
}
}
Ahora tanto CatcherA como CatcherB implementan el método Catch, por lo que el servicio que requiere un Catcher puede usar cualquiera de estos y realmente no importa cuál es. Entonces, un servicio estrechamente acoplado podría instanciar directamente un ejemplo atrapado
public CatchService
{
private CatcherA catcher = new CatcherA();
public void CatchService()
{
catcher.Catch();
}
}
Entonces, CatchService puede hacer exactamente lo que se ha propuesto hacer, pero usa CatcherA y siempre usará CatcherA. Está codificado, así que permanece ahí hasta que alguien venga y lo refactorice.
Ahora tomemos otra opción, llamada inyección de dependencia:
public CatchService
{
private ICatcher catcher;
public void CatchService(ICatcher catcher)
{
this.catcher = catcher;
catcher.Catch();
}
}
Entonces, el calss que instaura CatchService puede hacer lo siguiente:
CatchService catchService = new CatchService(new CatcherA());
o
CatchService catchService = new CatchService(new CatcherB());
Esto significa que el servicio Catch no está bien conectado a CatcherA o CatcherB.
Existen varias otras estrategias para acoplar servicios como este, como el uso de un marco de trabajo de IoC, etc.
En ciencias de la computación, hay otro significado para "acoplamiento flexible" que nadie más ha publicado aquí, así que ... Aquí va - ¡espero que me dé algunos votos para que esto no se pierda en el fondo del montón! SEGURAMENTE el tema de mi respuesta pertenece a cualquier respuesta integral a la pregunta ... A saber:
El término "acoplamiento suelto" entró en informática por primera vez como un término utilizado como un adjetivo sobre la arquitectura de la CPU en una configuración de múltiples CPU. Su término equivalente es "acoplamiento cerrado". Loose Coupling es cuando las CPU no comparten muchos recursos en común y el acoplamiento fuerte es cuando lo hacen.
El término "sistema" puede ser confuso aquí así que analice cuidadosamente la situación.
Generalmente, pero no siempre, múltiples CPU en una configuración de hardware en la que existen dentro de un sistema (como en cajas individuales de "PC") estarían estrechamente conectadas. Con la excepción de algunos sistemas de súper alto desempeño que tienen subsistemas que realmente comparten memoria principal a través de "sistemas", todos los sistemas divisibles están débilmente acoplados.
Los términos "Tightly Coupled" y "Looseely Coupled" se introdujeron antes de que se inventaran las CPU multi-threaded y multi-core, por lo que estos términos pueden necesitar algunos compañeros para articular la situación en la actualidad. Y, de hecho, hoy uno puede muy bien tener un sistema que abarque ambos tipos en un sistema global. Con respecto a los sistemas de software actuales, hay dos arquitecturas comunes, una de cada variedad, que son lo suficientemente comunes como para ser conocidas.
Primero, dado que de eso se trataba la pregunta, algunos ejemplos de sistemas débilmente acoplados:
- VaxClusters
- Clusters de Linux
Por el contrario, algunos ejemplos estrechamente combinados:
- Sistemas operativos Semetrical-Multi-Processing (SMP) - por ejemplo, Fedora 9
- CPU con múltiples subprocesos
- CPU multi-núcleo
En la informática actual, los ejemplos de ambos que operan en un solo sistema general no son infrecuentes. Por ejemplo, tome las CPU modernas Pentium de doble o cuádruple núcleo que ejecutan Fedora 9: estos son sistemas informáticos estrechamente acoplados. Luego, combine varios de ellos en un Clúster de Linux débilmente acoplado y ¡ahora tiene una informática débil y estrechamente conectada! ¡Oh, el hardware moderno no es maravilloso!
En un lenguaje simple, débilmente acoplado significa que no depende de que ocurra otro evento. Se ejecuta de forma independiente.
Es un concepto bastante general, por lo que los ejemplos de código no darán la imagen completa.
Un tipo aquí en el trabajo me dijo, "los patrones son como fractales, puedes verlos cuando haces un zoom realmente cerca, y cuando haces zoom hasta el nivel de arquitectura".
Leer la breve página de wikipedia puede darle una idea de esta generalidad:
http://en.wikipedia.org/wiki/Loose_coupling
En cuanto a un ejemplo de código específico ...
Aquí hay un acoplamiento débil con el que he trabajado recientemente, de las cosas de Microsoft.Practices.CompositeUI.
[ServiceDependency]
public ICustomizableGridService CustomizableGridService
{
protected get { return _customizableGridService; }
set { _customizableGridService = value; }
}
Este código declara que esta clase tiene una dependencia en un CustomizableGridService. En lugar de simplemente hacer referencia directamente a la implementación exacta del servicio, simplemente establece que requiere ALGUNA implementación de ese servicio. Luego, en tiempo de ejecución, el sistema resuelve esa dependencia.
Si eso no está claro, puedes leer una explicación más detallada aquí:
http://en.wikipedia.org/wiki/Dependency_injection
Imagine que ABCCustomizableGridService es la implementación que pretendo conectar aquí.
Si elijo hacerlo, puedo quitarlo y reemplazarlo con XYZCustomizableGridService, o StubCustomizableGridService sin ningún cambio en la clase con esta dependencia.
Si me hubiera referido directamente a ABCCustomizableGridService, entonces tendría que hacer cambios a esa / esas referencias / s para intercambiar otra implementación del servicio.
Lo sentimos, pero el "acoplamiento libre" no es un problema de codificación, es un problema de diseño. El término "acoplamiento libre" está íntimamente relacionado con el estado deseable de "alta cohesión", siendo opuesto pero complementario.
El acoplamiento flojo simplemente significa que los elementos de diseño individuales deben construirse de manera que se reduzca la cantidad de información innecesaria que necesitan saber sobre otros elementos de diseño.
La alta cohesión es algo así como "acoplamiento ajustado", pero la alta cohesión es un estado en el que los elementos de diseño que realmente necesitan conocerse entre sí están diseñados para que funcionen juntos de forma limpia y elegante.
El punto es que algunos elementos de diseño deben conocer detalles sobre otros elementos de diseño, por lo que deben diseñarse de esa manera, y no accidentalmente. Otros elementos de diseño no deben conocer detalles sobre otros elementos de diseño, por lo que deben diseñarse de esa manera, a propósito, en lugar de al azar.
Implementar esto se deja como un ejercicio para el lector :).
Propongo un muy simple Test of Code Coupling :
La pieza A de código está estrechamente unida a la Pieza B del código si existe alguna modificación posible en la Pieza B que forzaría cambios en la Pieza A para mantener la corrección.
La pieza A de código no está estrechamente unida a la Pieza B del código si no hay una modificación posible en la Pieza B que haría necesario un cambio en la Pieza A.
Esto te ayudará a verificar cuánto acoplamiento hay entre las piezas de tu código. para razonar sobre eso, vea esta publicación en el blog: http://marekdec.wordpress.com/2012/11/14/loose-coupling-tight-coupling-decoupling-what-is-that-all-about/
Puede leer más sobre el concepto genérico de "acoplamiento libre" .
En resumen, es una descripción de una relación entre dos clases, donde cada clase sabe lo mínimo sobre el otro y cada clase podría continuar funcionando bien si el otro está presente o no y sin dependencia de la implementación particular del otro clase.
Puede pensar en el acoplamiento (apretado o suelto) como literalmente la cantidad de esfuerzo que le llevaría separar una clase en particular de su dependencia de otra clase. Por ejemplo, si todos los métodos de tu clase tuvieran un pequeño bloque final en la parte inferior donde hiciste una llamada a Log4Net para registrar algo, entonces dirías que tu clase estaba estrechamente unida a Log4Net. Si su clase en cambio contiene un método privado llamado LogSomething que era el único lugar que llamaba al componente Log4Net (y los otros métodos todos llamados LogSomething en su lugar), entonces diría que su clase estaba vagamente acoplada a Log4Net (porque no tomaría mucho esfuerzo para sacar Log4Net y reemplazarlo con algo más).
Tal vez la mejor metáfora es el matrimonio.
Cuando no estás casado, estás muy unido.
Puede dejar a su pareja más fácilmente.
Cuando "estás" casado, estás muy unido.
Por ejemplo, en algunos países, debe pagar una pensión alimenticia cuando abandona a su pareja.
Usaré Java como ejemplo. Digamos que tenemos una clase que se ve así:
public class ABC
{
public void doDiskAccess() {...}
}
Cuando llamo a la clase, tendré que hacer algo como esto:
ABC abc = new ABC();
abc. doDiskAccess();
Hasta aquí todo bien. Ahora digamos que tengo otra clase que se ve así:
public class XYZ
{
public void doNetworkAccess() {...}
}
Se ve exactamente igual que ABC, pero digamos que funciona en la red en lugar de en el disco. Entonces, vamos a escribir un programa como este:
if(config.isNetwork()) new XYZ().doNetworkAccess();
else new ABC().doDiskAccess();
Eso funciona, pero es un poco difícil de manejar. Podría simplificar esto con una interfaz como esta:
public interface Runnable
{
public void run();
}
public class ABC implements Runnable
{
public void run() {...}
}
public class XYZ implements Runnable
{
public void run() {...}
}
Ahora mi código puede verse así:
Runnable obj = config.isNetwork() ? new XYZ() : new ABC();
obj.run();
¿Ves cuánto más limpio y simple de entender es eso? Acabamos de entender el primer principio básico del acoplamiento flexible: la abstracción. La clave de aquí es asegurar que ABC y XYZ no dependan de ningún método o variable de las clases que los llamen. Eso permite que ABC y XYZ sean API completamente independientes. O en otras palabras, están "desacoplados" o "débilmente acoplados" de las clases de padres.
Pero, ¿y si necesitamos comunicación entre los dos? Bueno, entonces podemos usar abstracciones adicionales como un Modelo de eventos para asegurarnos de que el código padre nunca tenga que coincidir con las API que ha creado.
Los iPods son un buen ejemplo de acoplamiento ajustado: una vez que la batería se agota, también puedes comprar un iPod nuevo porque la batería está soldada y no se suelta, lo que hace que la sustitución sea muy costosa. Un jugador débilmente acoplado permitiría cambiar la batería sin esfuerzo.
Lo mismo , 1: 1, va para el desarrollo de software.