What is “loose coupling?” Please provide examples

馋奶兔 提交于 2019-11-26 03:00:13

问题


I can\'t seem to grok the concept of \"loose coupling.\" I suppose it doesn\'t help that the word \"loose\" usually has a negative connotation, so I always forget that loose coupling is a good thing.

Will somebody please show some \"before\" and \"after\" code (or pseudocode) that illustrates this concept?


回答1:


Consider a simple shopping cart application that uses a CartContents class to keep track of the items in the shopping cart and an Order class for processing a purchase. The Order needs to determine the total value of the contents in the cart, it might do that like so:

Tightly Coupled Example:

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;
    }
}

Notice how the OrderTotal method (and thus the Order class) depends on the implementation details of the CartContents and the CartEntry classes. If we were to try to change this logic to allow for discounts, we'd likely have to change all 3 classes. Also, if we change to using a List collection to keep track of the items we'd have to change the Order class as well.

Now here's a slightly better way to do the same thing:

Less Coupled Example:

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);
    }
}

The logic that is specific to the implementation of the cart line item or the cart collection or the order is restricted to just that class. So we could change the implementation of any of these classes without having to change the other classes. We could take this decoupling yet further by improving the design, introducing interfaces, etc, but I think you see the point.




回答2:


Many integrated products (especially by Apple) such as iPods, iPads are a good example of tight coupling: once the battery dies you might as well buy a new device because the battery is soldered fixed and won't come loose, thus making replacing very expensive. A loosely coupled player would allow effortlessly changing the battery.

The same goes for software development: it is generally (much) better to have loosely coupled code to facilitate extension and replacement (and to make individual parts easier to understand). But, very rarely, under special circumstances tight coupling can be advantageous because the tight integration of several modules allows for better optimisation.




回答3:


I'll use Java as an example. Let's say we have a class that looks like this:

public class ABC
{
   public void doDiskAccess() {...}
}

When I call the class, I'll need to do something like this:

ABC abc = new ABC();

abc. doDiskAccess();

So far, so good. Now let's say I have another class that looks like this:

public class XYZ
{
   public void doNetworkAccess() {...}
}

It looks exactly the same as ABC, but let's say it works over the network instead of on disk. So now let's write a program like this:

if(config.isNetwork()) new XYZ().doNetworkAccess();
else new ABC().doDiskAccess();

That works, but it's a bit unwieldy. I could simplify this with an interface like this:

public interface Runnable
{
    public void run();
}

public class ABC implements Runnable
{
   public void run() {...}
}

public class XYZ implements Runnable
{
   public void run() {...}
}

Now my code can look like this:

Runnable obj = config.isNetwork() ? new XYZ() : new ABC();

obj.run();

See how much cleaner and simpler to understand that is? We've just understood the first basic tenet of loose coupling: abstraction. The key from here is to ensure that ABC and XYZ do not depend on any methods or variables of the classes that call them. That allows ABC and XYZ to be completely independent APIs. Or in other words, they are "decoupled" or "loosely coupled" from the parent classes.

But what if we need communication between the two? Well, then we can use further abstractions like an Event Model to ensure that the parent code never needs to couple with the APIs you have created.




回答4:


Sorry, but "loose coupling" is not a coding issue, it's a design issue. The term "loose coupling" is intimately related to the desirable state of "high cohesion", being opposite but complementary.

Loose coupling simply means that individual design elements should be constructed so the amount of unnecessary information they need to know about other design elements are reduced.

High cohesion is sort of like "tight coupling", but high cohesion is a state where design elements that really need to know about each other are designed so that they work together cleanly and elegantly.

The point is, some design elements should know details about other design elements, so they should be designed that way, and not accidentally. Other design elements should not know details about other design elements, so they should be designed that way, purposefully, instead of randomly.

Implementing this is left as an exercise for the reader :) .




回答5:


Tightly coupled code relies on a concrete implementation. If I need a list of strings in my code and I declare it like this (in Java)

ArrayList<String> myList = new ArrayList<String>();

then I'm dependent on the ArrayList implementation.

If I want to change that to loosely coupled code, I make my reference an interface (or other abstract) type.

List<String> myList = new ArrayList<String>();

This prevents me from calling any method on myList that's specific to the ArrayList implementation. I'm limited to only those methods defined in the List interface. If I decide later that I really need a LinkedList, I only need to change my code in one place, where I created the new List, and not in 100 places where I made calls to ArrayList methods.

Of course, you can instantiate an ArrayList using the first declaration and restrain yourself from not using any methods that aren't part of the List interface, but using the second declaration makes the compiler keep you honest.




回答6:


The degree of difference between answers here shows why it would be a difficult concept to grasp but to put it as simply as I can describe it:

In order for me to know that if I throw a ball to you, then you can catch it I really dont need to know how old you are. I dont need to know what you ate for breakfast, and I really dont care who your first crush was. All I need to know is that you can catch. If I know this, then I dont care if its you I am throwing a ball to you or your brother.

With non-dynamic languages like c# or Java etc, we accomplish this via Interfaces. So lets say we have the following interface:

public ICatcher
{
   public void Catch();
}

And now lets say we have the following classes:

public CatcherA : ICatcher
{
   public void Catch()
   {
      console.writeline("You Caught it");
   }

}
public CatcherB : ICatcher
{
   public void Catch()
   {
      console.writeline("Your brother Caught it");
   }

}

Now both CatcherA and CatcherB implement the Catch method, so the service that requires a Catcher can use either of these and not really give a damn which one it is. So a tightly coupled service might directly instanciate a catched i.e.

public CatchService
{
   private CatcherA catcher = new CatcherA();

   public void CatchService()
   {
      catcher.Catch();
   }

}

So the CatchService may do exactly what it has set out to do, but it uses CatcherA and will always user CatcherA. Its hard coded in, so its staying there until someone comes along and refactors it.

Now lets take another option, called dependency injection:

public CatchService
{
   private ICatcher catcher;

   public void CatchService(ICatcher catcher)
   {
      this.catcher = catcher;
      catcher.Catch();
   }
}

So the calss that instansiates CatchService may do the following:

CatchService catchService = new CatchService(new CatcherA());

or

CatchService catchService = new CatchService(new CatcherB());

This means that the Catch service is not tightly coupled to either CatcherA or CatcherB.

There are several other stratergies for loosly coupling services like this such as the use of an IoC framework etc.




回答7:


You can think of (tight or loose) coupling as being literally the amount of effort it would take you to separate a particular class from its reliance on another class. For example, if every method in your class had a little finally block at the bottom where you made a call to Log4Net to log something, then you would say your class was tightly coupled to Log4Net. If your class instead contained a private method named LogSomething which was the only place that called the Log4Net component (and the other methods all called LogSomething instead), then you would say your class was loosely coupled to Log4Net (because it wouldn't take much effort to pull Log4Net out and replace it with something else).




回答8:


Definition

Essentially, coupling is how much a given object or set of object relies on another object or another set of objects in order to accomplish its task.

High Coupling

Think of a car. In order for the engine to start, a key must be inserted into the ignition, turned, gasoline must be present, a spark must occur, pistons must fire, and the engine must come alive. You could say that a car engine is highly coupled to several other objects. This is high coupling, but it's not really a bad thing.

Loose Coupling

Think of a user control for a web page that is responsible for allowing users to post, edit, and view some type of information. The single control could be used to let a user post a new piece of information or edit a new piece of information. The control should be able to be shared between two different paths - new and edit. If the control is written in such a way that it needs some type of data from the pages that will contain it, then you could say it's too highly coupled. The control should not need anything from its container page.




回答9:


It's a pretty general concept, so code examples are not going to give the whole picture.

One guy here at work said to me, "patterns are like fractals, you can see them when you zoom in really close, and when you zoom way out to the architecture level."

Reading the brief wikipedia page can give you a sense of this generalness:

http://en.wikipedia.org/wiki/Loose_coupling

As far as a specific code example...

Here's one loose coupling I've worked with recently, from the Microsoft.Practices.CompositeUI stuff.

    [ServiceDependency]
    public ICustomizableGridService CustomizableGridService
    {
        protected get { return _customizableGridService; }
        set { _customizableGridService = value; }
    }

This code is declaring that this class has a dependency on a CustomizableGridService. Instead of just directly referencing the exact implementation of the service, it simply states that it requires SOME implementation of that service. Then at runtime, the system resolves that dependency.

If that's not clear, you can read a more detailed explanation here:

http://en.wikipedia.org/wiki/Dependency_injection

Imagine that ABCCustomizableGridService is the imlpementation I intend to hook up here.

If I choose to, I can yank that out and replace it with XYZCustomizableGridService, or StubCustomizableGridService with no change at all to the class with this dependency.

If I had directly referenced ABCCustomizableGridService, then I would need to make changes to that/those reference/s in order to swap in another service implementation.




回答10:


Coupling has to do with dependencies between systems, which could be modules of code (functions, files, or classes), tools in a pipeline, server-client processes, and so forth. The less general the dependencies are, the more "tightly coupled" they become, since changing one system required changing the other systems that rely on it. The ideal situation is "loose coupling" where one system can be changed and the systems depending on it will continue to work without modification.

The general way to achieve loose coupling is through well defined interfaces. If the interaction between two systems is well defined and adhered to on both sides, then it becomes easier to modify one system while ensuring that the conventions are not broken. It commonly occurs in practice that no well-defined interface is established, resulting in a sloppy design and tight coupling.

Some examples:

  • Application depends on a library. Under tight coupling, app breaks on newer versions of the lib. Google for "DLL Hell".

  • Client app reads data from a server. Under tight coupling, changes to the server require fixes on the client side.

  • Two classes interact in an Object-Oriented hierarchy. Under tight coupling, changes to one class require the other class to be updated to match.

  • Multiple command-line tools communicate in a pipe. If they are tightly coupled, changes to the version of one command-line tool will cause errors in the tools that read its output.




回答11:


Coupling refers to how tightly different classes are connected to one another. Tightly coupled classes contain a high number of interactions and dependencies.

Loosely coupled classes are the opposite in that their dependencies on one another are kept to a minimum and instead rely on the well-defined public interfaces of each other.

Legos, the toys that SNAP together would be considered loosely coupled because you can just snap the pieces together and build whatever system you want to. However, a jigsaw puzzle has pieces that are TIGHTLY coupled. You can’t take a piece from one jigsaw puzzle (system) and snap it into a different puzzle, because the system (puzzle) is very dependent on the very specific pieces that were built specific to that particular “design”. The legos are built in a more generic fashion so that they can be used in your Lego House, or in my Lego Alien Man.

Reference: https://megocode3.wordpress.com/2008/02/14/coupling-and-cohesion/




回答12:


Two components are higly coupled when they depend on concrete implementation of each other.

Suppose I have this code somewhere in a method in my class:

this.some_object = new SomeObject();

Now my class depends on SomeObject, and they're highly coupled. On the other hand, let's say I have a method InjectSomeObject:

void InjectSomeObject(ISomeObject so) { // note we require an interface, not concrete implementation
  this.some_object = so;
}

Then the first example can just use injected SomeObject. This is useful during testing. With normal operation you can use heavy, database-using, network-using classes etc. while for tests passing a lightweight, mock implementation. With tightly coupled code you can't do that.

You can make some parts of this work easer by using dependency injection containers. You can read more about DI at Wikipedia: http://en.wikipedia.org/wiki/Dependency_injection.

It is sometimes easy to take this too far. At some point you have to make things concrete, or your program will be less readable and understandable. So use this techniques mainly at components boundary, and know what you are doing. Make sure you are taking advantage of loose coupling. If not, you probably don't need it in that place. DI may make your program more complex. Make sure you make a good tradeoff. In other words, maintain good balance. As always when designing systems. Good luck!




回答13:


Consider a Windows app with FormA and FormB. FormA is the primary form and it displays FormB. Imagine FormB needing to pass data back to its parent.

If you did this:

class FormA 
{
    FormB fb = new FormB( this );

    ...
    fb.Show();
}

class FormB 
{
    FormA parent;

    public FormB( FormA parent )
    {
        this.parent = parent;
    }     
}

FormB is tightly coupled to FormA. FormB can have no other parent than that of type FormA.

If, on the other hand, you had FormB publish an event and have FormA subscribe to that event, then FormB could push data back through that event to whatever subscriber that event has. In this case then, FormB doesn't even know its talking back to its parent; through the loose coupling the event provides it's simply talking to subscribers. Any type can now be a parent to FormA.

rp




回答14:


In computer science there is another meaning for "loose coupling" that no one else has posted about here, so... Here goes - hopefully you'll give me some votes up so this isn't lost at the bottom of the heap! SURELY the subject of my answer belongs in any comprehensive answer to the question... To wit:

The term "Loose Coupling" first entered computing as a term used as an adjective regarding CPU architecture in a multi-CPU configuration. Its counterpart term is "tight coupling". Loose Coupling is when CPUs do not share many resources in common and Tight Coupling is when they do.

The term "system" can be confusing here so please parse the situation carefully.

Usually, but not always, multiple CPUs in a hardware configuration in which they exist within one system (as in individual "PC" boxes) would be tightly coupled. With the exception of some super-high-performance systems that have subsystems that actually share main memory across "systems", all divisible systems are loosely coupled.

The terms Tightly Coupled and Loosely Coupled were introduced before multi-threaded and multi-core CPUs were invented, so these terms may need some companions to fully articulate the situation today. And, indeed, today one may very well have a system that encompases both types in one overall system. Regarding current software systems, there are two common architectures, one of each variety, that are common enough these should be familliar.

First, since it was what the question was about, some examples of Loosely Coupled systems:

  • VaxClusters
  • Linux Clusters

In contrast, some Tightly Coupled examples:

  • Semetrical-Multi-Processing (SMP) Operating systems - e.g. Fedora 9
  • Multi-threaded CPUs
  • Multi-Core CPUs

In today's computing, examples of both operating in a single overall system is not uncommon. For example, take modern Pentium dual or quad core CPUs running Fedora 9 - these are tightly-coupled computing systems. Then, combine several of them in a loosely coupled Linux Cluster and you now have both loosely and tightly coupled computing going on! Oh, isn't modern hardware wonderful!




回答15:


In simple language, loosely coupled means it doesn’t depend on other event to occur. It executes independently.




回答16:


Some long answers here. The principle is very simple though. I submit the opening statement from wikipedia:

"Loose coupling describes a resilient relationship between two or more systems or organizations with some kind of exchange relationship.

Each end of the transaction makes its requirements explicit and makes few assumptions about the other end."




回答17:


I propose a very simple Test of Code Coupling:

  1. Piece A of code is tightly coupled to Piece B of code if there exists any possible modification to the Piece B that would force changes in Piece A in order to keep correctness.

  2. Piece A of code is not tightly coupled to Piece B of code if there is no possible modification to the Piece B that would make a change to Piece A necessary.

This will help you to verify how much coupling there is between the pieces of your code. for reasoning on that see this blog post: http://marekdec.wordpress.com/2012/11/14/loose-coupling-tight-coupling-decoupling-what-is-that-all-about/




回答18:


When you create an object of a class using new keyword in some other class, you are actually doing tight coupling (bad practice) instead you should use loose coupling which is a good practice

---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();
}

---MainClass---

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 

}
}

Explanation:

In above example, we have two classes A and B

Class B implements Interface i.e. InterfaceClass.

InterfaceClass defines a Contract for B class as InterfaceClass have abstract methods of B class that can be access by any other class for example A.

In Class A we have display method which can except object of class which implements InterfaceClass (in our case it is B class). And on that object method of class A is calling display() and getVar() of class B

In MainClass we have created object of Class A and B. And calling display method of A by passing object of B class i.e. objb. Display method of A will be called with object of B class.

Now talking about loose coupling. Suppose in future you have to change the name of Class B to ABC then you do not have to change its name in display method of class B, just make the object of new (ABC class) and pass it to the display method in MailClass. You do not have to change anything in Class A

ref: http://p3lang.com/2013/06/loose-coupling-example-using-interface/




回答19:


You can read more about the generic concept of "loose coupling".

In short, it's a description of a relationship between two classes, where each class knows the very least about the other and each class could potentially continue to work just fine whether the other is present or not and without dependency on the particular implementation of the other class.




回答20:


Loose coupling, in general, is 2 actors working independently of each other on the same workload. So if you had 2 web servers using the same back-end database, then you would say that those web servers are loosely coupled. Tight coupling would be exemplified by having 2 processors on one web server... those processors are tightly coupled.

Hope that's somewhat helpful.



来源:https://stackoverflow.com/questions/226977/what-is-loose-coupling-please-provide-examples

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!