Saturday, December 18, 2010

What is loose coupling?

This weekend, I started to wonder what loose coupling is. I came to the following conclusion: the degree of coupling is in proportion to the amount of knowledge required to use a specific implementation of an interface. Let me explain how I came to this definition.

For the purpose of this discussion, I define the concepts of a client and a provider. A client is a piece of software that uses the services of a provider. In some cases, a client needs to be able to switch between different providers. A client can also use more than 1 provider at the time.

An example of a provider is a database provider, which provides access to a particular type of database (Oracle, SQL server). Usually, an application uses one database provider at the time. Another example is an error handling provider. An application often uses more than one error handling provider, for example, a provider which writes to a log file and a provider which shows an error message to the user. In this example, loose coupling allows the client to swap database providers and add/remove error handling providers with few or no changes to the clients source code.

I think many developers understand the merits of loose coupling. The question is: how do we achieve loose coupling? I think there are some concepts that are related to loose coupling, but do not necessarily influence or guarantee loose coupling.

Sometimes the degree of coupling is confused with the method of invocation. Methods can be bound early or late or they can be invoked via some type of messaging system. The method of invocation has nothing to do with loose or tight coupling. The fact that I can invoke a method through a messaging system or through reflection does not have any relation to the amount of knowledge I need to use a specific provider.

An example is the way a connection string is passed to a database provider. The connection string is passed as a string (of course), so the API is very generic. However, the contents of the string is usually dependent on a specific database provider. Each database provider accepts a different set of parameters in the connection string. So, the content of the connection string causes tight coupling between the client and the database provider.

Another point is the physical deployment: monolothic (1 EXE) vs. componentized (EXE + DLLs) vs. distributed (multiple processes or machines). Internally, a monolothic application can be composed of loosely coupled classes. A componentized application can have a lot of coupling between the client code and specific provider implementations.

As an example, consider an application that calls a webservice. A webservice usually implements a very specific set of methods and the application depends on it. The coupling would be no different if the same interface was implemented as part of a monolothic application.

The last point I want to make is about narrow interfaces (few methods) vs. rich interfaces (many methods). It is sometimes thought that a narrow interface promotes loose coupling. However, the way a connection string is passed to a database provider could be seen as a narrow interface. We know that a connection string increases coupling. This is also true for any SQL query being passed through an ODBC interface: the SQL query must be compatible with the flavor of SQL supported by the database provider. My conclusion is that there is no direction relationship between narrow interfaces and loose or tight coupling.

Another point I want to make is that it doesn't seem to make sense to say that an entire application must be loosely coupled. It has to be more concrete than that: loosely coupled from the database provider, error handling, operating system, web browser, etc. To try to be loosely coupled from everything will be an overkill and may lead to overly complicated and poor performing software.

As an example of loose coupling, I will give NHibernate as an example. NHibernate allowed me to switch between Oracle and Sql Server without changing any code. NHibernate enabled my application to be loosely coupled from the database provider (at least those 2 providers).

I am not saying that messaging systems, componentized software and narrow interfaces are useless. My point is that these concepts do not guarantee a loosely coupled system. The devil is in the details: how to define and implement interfaces that can be used in a way that the client software requires limited knowledge of the specific implementations of those interfaces.

No comments: