Reflection and Package Coupling
I sometimes make genuinely loose coupling by hiding dependencies from the Java compiler and the Java Virtual Machine class loader. Reflection allows you to delay certain decisions until runtime, and sometimes you can deliver code that's very loosely coupled indeed.
For example, suppose you have a user interface that brings up various other windows. The customer may have paid for only some of those packages to be installed. Here's how you disable a menu item when the corresponding class isn't installed:
JMenuItem item = ... try { Class.forName("optionalPackage.MyClass"); } catch(ClassNotFoundException e) { item.setEnabled(false); }
If the class isn't present, it will be disabled. To actually bring up the window, you have to be able to obtain and use an instance of MyClass. You can do that by taking advantage of the type hierarchy:
JFrame ui = (JFrame) Class.forName("optionalPackage.MyClass"). getConstructors()[0].newInstance(new Object[] { }); ui.setVisible(true);
Because you know that MyClass is a JFrame, you can create instances of it, and even manipulate it. The code won't work in installations where the class isn't present, but that's why we disabled the button.
You might leave the button enabled, and encourage users to buy some add-on package when it's clicked. To deliver the add-on package, you'd only need to deliver the extra code and add it to the classpath. You wouldn't need to change any of the installed code. That's a good thing because it ensures that the user continues to use the existing delivered code, including any patches that have been installed. You don't have to match the exact version of the code the user has.
In some ways, this goes against my usual principles of coding. Any typos won't be caught until runtime. If you change the constructors for MyClass, the compiler won't catch them for you. And it doesn't really decrease the coupling; it just hides the coupling from the compiler and class loader.
In software engineering terms, this is probably worse code rather than better, but it solves certain problems more easily than the alternative, which is to keep several different versions of the program with different capabilities. That would lead to even worse coupling, because you'd be maintaining several nearly identical pieces of code that must all be kept in line.