ChatGPT解决这个技术问题 Extra ChatGPT

What is "loose coupling?" Please provide examples

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?

I'm not sure how this can be divorced from a particular language because part of the concept is how one uses a language to keep code units from getting too entangled with each other. I also note that several of the answers resort to code to illustrate the concept.
Disagree completely, the practicalities can be illustrated differently in different languages, and it is especially apparent in dynamic vs compiled languages. But the concept is the same either way.
Loose vs Tight and Coupling vs Decoupling. Tagging along is not a good thing always, sometimes good/sometimes not good...As we should have independence of our own, so do the classes.

d
djvg

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<CartEntry> 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.


Could you expand the answer introducing further decoupling through dependency injection (though constructor should be ideal)?
@Wedge, I see no reason for the Order to have any knowledge of a Cart. Live shopping and facturation are two different contexts. When the client is ready to pay, you could have a process responsible to translate CartEntries into something meaninful for the Order. This way the Order class would also be instanciated and used without a Cart.
@Wedge Could you react to my comment please?
Well & simply explained. I started to see the light at "..Notice how the OrderTotal method (and thus the Order class) depends on the implementation details of the CartContents and the CartEntry classes.."
@JoSo, However in these days, more and more architects agree that this overly object-oriented style is bad practice [citation needed] :)
K
Konrad Rudolph

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.


Is loose or tight coupling good? How to decide upon to implement loose coupling or tight coupling?
thank's for this great example. i spent a lot of time on other answers but non worth like this
Sreekanth Karumanaghat, would you rather spend less money just for a battery and have a working iPod again or you would prefer to spend a lot of money for a whole iPod?
@SreekanthKarumanaghat Typically loose coupling is better because it promotes modularity and thus makes code easier to maintain. If you tightly couple your classes/libraries/code, then any small change you make in one class will have to be implemented in all of the classes that use it.
A
AmbroseChapel

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.


This example still has quite a high level of coupling. Just now instead of being coupled to a single class, there are two to choose from. This example assumes there will be only ever two "Runnable" implementations available. In which case an interface isn't even required.
These Run* and Do* named methods are a huge code smell for me. You might as well refactor this into using IHandleStuff or IFrobnicateThings. Runnable is far, far too generic a term in this case.
Owen, baby steps my friend. If he doesn't understand coupling, how is he going to understand what a factory pattern buys him? This code example puts him on the right track. The intent is that he'll better understand the problem and realize how to abstract out the creation of the objects.
Would this be more readable if ABC and XYZ were renamed something like DiskAccessor and NetworkAccessor? I dunno no java ...
D
David M. Karr

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 :) .


B
Bill the Lizard

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.


This hardly relates to what "coupling" actually is. See stackoverflow.com/questions/39946/coupling-and-cohesion for some good answers.
@Rogerio: I think it relates. Maybe you're confused by other definitions of "coupling" but you you should read Loose coupling. "Strong coupling occurs when a dependent class contains a pointer directly to a concrete class which provides the required behavior... Loose coupling occurs when the dependent class contains a pointer only to an interface, which can then be implemented by one or many concrete classes." In my example, my code would be loosely coupled to an ArrayList through the List interface.
@Bill: Thanks for the link, but that article seems completely bogus to me. It appears to "reinterpret" ideas from another domain (organizational behavior), giving a definition of "loose coupling" that contradicts the original software-specific definition by Larry Constantine: en.wikipedia.org/wiki/Coupling_(computer_science).
@Rogerio: Constantine's paper was written in 1974. It's probable that a lot of reinterpreting has gone on in the last 36 years, but I don't think that Wikipedia article is its source. For example, patterns like Dependency Injection rely on interfaces to loosen coupling just as I describe in my answer.
@Bill: Sure, Constantine's work on Structured Design is old stuff, but I fail to see any reason to invalidate or radically reinterpret it. The original concepts of cohesion and coupling are at the core of Object Oriented Design, just as much as inheritance and polymorphism. DI (a better article is martinfowler.com/articles/injection.html) is merely a technique for the external configuration of plug-ins (abstract interfaces with implementations selected at runtime through configuration code); another such technique is the Service Locator pattern. DI and coupling are independent concepts.
d
djvg

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 instantiate 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 calls that instantiate 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 strategies for loosely coupling services like this such as the use of an IoC framework etc.


I like this example better than the other ones, since this kind of thing happens in the business world in a day to day basis. If Class Person runs, Class Cat runs, Class Dog runs, etc. It'd be a huge program to detail all those classes that runs. But using an interface called runs, where each class does the same thing, makes your program much more flexible and manageable. :)
Explained really well sir, just to clear in the last example you did it with dependency injection, and the first example is also loosely coupled?
Wow, old answer commented on :). So the answer is no, the first example has a hard dependency on CatcherA so they are strongly coupled
thanks, Owen Sir, Your example really clarifies the doubts about the loose and tight coupling.
M
MusiGenesis

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).


So, "loosely coupled" actually means less dependencies? Am I correct?
It's not really related to the total number of dependencies one particular class has. Coupling (tight or loose) is actually a property of the relationship between two classes. So you might have one class which is loosely coupled to 100 other classes, or another class which is tightly coupled to just 2 other classes (for example).
Bad example, I would say: what you have is code duplication, not tight coupling. And with a good refactoring tool, such duplication could be eliminated in seconds.
@Rogerio: it's probably not the best example. I like 64BitBob's answer better, myself.
T
Tom

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.


r
rice

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.


P
Parappa

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.


u
user1263981

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/


r
rp.

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


P
Paweł Hajdan

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!


R
Richard T

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!


h
halfer

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


That's not really true - as mentioned in other examples, it's not so much about whether or not there is a relationship between entities (modules, classes), but whether or not they can be easily swapped out for other classes implementing the same functionality. e.g a ViewModel might work with different models, but it definitely won't work without one
eh, not very helpful
B
Ben Aston

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."


If it was so simple, we wouldn't have this discussion.
M
Marek Dec

I propose a very simple Test of Code Coupling:

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. 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/


A
Abhishek Aggarwal

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/


F
Franci Penov

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.


S
Steve

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.


I see what you are going for, but still not a very good way of explaining that tightly coupled is when two 'things' are interdependant on each other, compared to loosely coupled when two 'things' exists independently - where one 'thing' can be changed out without changes to the other 'thing'...
Steve, you are EXACTLY right - you didn't deserve ANY of the down-votes. It's just your audience wasn't ready for your terminology and too many on this site have a habit of voting down what they don't understand or what is unfamiliar. -shrug-
I think Steve missed the point. Loose coupling does not always imply 2 actors working independently on the same workload.