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.