Writing decoupled software components means writing them so that they depend on each other as little as possible. If one of your classes uses another class directly, they will inevitably be coupled together. Use one, and you will have to use the other. To decouple components, we normally think of letting our classes talk to interfaces instead of concrete classes. Any subclass can hide behind the interface, so the coupling between the classes is reduced. One good example of “talking to interfaces” is the Factory method design pattern. Instead of using
new on a concrete class, you will ask an interface to create the object for you. But decoupling is more than just talking to interfaces.
When I first started programming Java, I came across Spring and its inversion of control containers (IoC). Compared to the Factory method above, Spring IoC takes object creation decoupling a step further. By using XML files, you specify which object to create and which arguments to give to the constructor (e.g. other objects). Coming from C++, it was pure magic the first time I saw it. Nowhere in the code do your classes refer to each other. Now, that is decoupling!
Later, I started working with OSGi. It is a Java framework which allows for independent life-cycles of modules (called bundles). For example, you could replace a bundle with a newer version during run-time. Other bundles won’t notice they are communicating with something new. Bundles communicate with each other by publishing and consuming services, which are just plain interfaces. XML is used to specify what services are published and consumed by a bundle. Similar to above, individual modules never refer to each other in the code. Again, that is decoupling.
Web APIs, such as a REST or SOAP API over HTTP, are often used to provide access to web or cloud services. Also, more and more enterprises expose their internal sub-systems as web services. To assemble software using web APIs, you can combine software components that may execute anywhere in the world. The pattern here seems to be that the larger the software component, the more decoupling is possible (or required?). Decoupled components through interfaces needed to be compiled together. Decoupled components through web APIs need not even be in the same time zone. The downside is that more powerful decoupling techniques require a lot of work.
Combining decoupling on different levels will make your system more resilient to change and open up for use in new and unexpected ways. The next time you think of ways to make your code more loosely coupled, remember that decoupling is not only about interface classes!