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?
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:
And now lets say we have the following classes:
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.
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:
So the calss that instansiates CatchService may do the following:
or
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.
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:
In contrast, some Tightly Coupled examples:
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!
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.
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 :) .
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.
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:
Now my class depends on SomeObject, and they're highly coupled. On the other hand, let's say I have a method InjectSomeObject:
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!