For the history of coupling, how it was born and who is responsible for that as well as why developers usually have problems with understanding it, you should go to A brief history of coupling and cohesion. Here we are going to focus on the concept itself and its types.
Based on our experience, we probably know that coupling is a degree of the relationship between classes – the strength of connections between them. The value of this strength can tell us what a probability of the need for changes in other classes than the class that we are currently working on is. At the same time, it impacts the costs of development and maintenance.
Because of that, we should pursue loose coupling, so one would be able to easily analyze/debug/maintain any class, without detailed knowledge about other classes within the system. The looser the coupling is, the more independent the classes are. How can we define a degree of relationship between the two classes? To answer this question, we should take a look at communication between them. How do they do it? What does the message look like? When we know this, we can define what type of coupling it is and what it entails.
It was Glenford Myers who proposed the most common division of coupling into types in his book Reliable Software Through Composite Design. He defined six types, and now we are going to give them our attention. Let’s go from those considered as the least to those that are the most welcome.
Types of coupling
Content Coupling
Also known as “pathological coupling”.That’s because it usually occurs when one class directly uses private members of the other class. Objects of the first class are changing an internal state and behavior of objects of the second class. From the message’s point of view, it looks like the sender forces the message on the recipient, who is unable to resist.
In our daily dev life, good examples of it could be:
– not respecting access modifiers,
– reflection,
– monkey patching.
The degree of how much we know about the other class is the highest as we know literally everything.
Listing 1. The object of the Image probably would prefer to set its description field on its own.
Class<?> clazz = Image.class; Object imageInstance = clazz.newInstance(); Field descriptionField = imageInstance .getClass().getDeclaredField("description"); descriptionField.setAccessible(true); descriptionField .set(imageInstance, "Pen Pineapple Apple Pen");
Common Coupling
When classes change and communicate via a shared global state, we probably have a brush with common coupling. It means that when the state is changed in runtime, we may have no idea what objects rely on it and how it would impact their behavior.
The name itself is derived from the COMMON statement used in Fortran, where it means a piece of memory shared between different modules. Because of that, they are coupled to each other through this common environment.
It’s easy to notice that in this kind of coupling, we need to be extremely careful while changing the global state as it could cause changes in places that we would not expect. It requires an immense knowledge of all classes that depend on that state. However, it’s not only about the state but also about the data structure – any change in it causes changes in all dependent classes.
External Coupling
Let’s consider now the case when the structure of the message used in communication between classes comes from the outside of our system – the structure is external, so is coupling.
A good example could be using classes from third-party libraries: because of doing so, we become tightly coupled to them, and we lose control of any changes introduced in their next releases. Another example could be some external protocol or data format used in communication.
Listing 2. External coupling – let’s imagine now removing the getAmount() method (that is used everywhere in our system) by an author of popular money class provider library.
import popular.money.class.provider.Money import popular.money.class.provider.MoneyAmount public class Foo { public MoneyAmount receive(Money money) { //method body return money.getAmount(); } }
Just to make it clear – we will always need third-party libraries and will always integrate with something outside of our system using some external standard or protocol. However, the most important thing is to disallow those libraries/integrations to spread all around the system. The best idea would be to use them to do their job but then to block them out using our abstractions. The idea behind it is not to “rewrite everything” when, for example, some external library went into a maintenance-only mode, but to change/replace the implementation in some specific places.
Control Coupling
Imagine the situation when you are writing a piece of code and you call a method with some kind of flag as a parameter. Moreover, based on the value of the flag, you can expect different behavior and result. You as a caller have to know quite well what’s going on inside the called method, and the method/class itself is not a black box for you anymore. At this stage, you are more like a coordinator as you say what has to be done and what you expect in return. The described scenario is a typical example of control coupling, where one class passes elements of control to another one. What’s important here, the class that passes those arguments does it deliberately as it wants to achieve specific results. The most common cases for control coupling are methods that take the above-mentioned flags or use switch statements.
In OOP, objects should decide what to do based on their internal state and received data and not on an external flag passed by someone with a view to doing something.
Listing 3. Control coupling – it could be a good idea to split this method into two separate methods.
public void save(boolean validationRequired) { if (validationRequired) { validate(); store(); } else { store(); } }
Stamp Coupling and Data Coupling
When communication between classes is not either pathological (content coupling) or through the shared global state (common coupling), neither via external protocol nor structure (external coupling) and it has no elements of control (control coupling), then we have probably ended up with one of the two types that put the message and its structure on the pedestal.
In the first type (stamp coupling), classes use data structure (our own and not from the third-part lib) in communication. Usually, it is some kind of DTO. The recipient can decide whether he wants to use all data from the structure or just a part of it because the rest can be totally useless in a specific context.
For example, in different applications, we can often find classes whose name ends in “details” or “data”. Does it resonate with you? So let’s take a look at EmployeeDetails class, as you probably suspect you could find everything about an employee there – the sky is the limit. Let’s assume now that we have the method that takes an object of this class as a parameter and should return an employee’s address, based only on the employee’s id from inside of the whole structure. Now we could argue that there is no coupling between caller and recipient as we pass some set of data, the recipient takes what it needs, and that’s it. However, there is coupling to the whole structure of the passed data. There could be a case when a change occurs in a part of the structure that is not used by the recipient, but some adjustments are still required.
This kind of coupling can also lead to the creation of artificial data structures, that would hold unrelated data. Then frivolous adding different items to such data “bags” could become a common practice in our code.
Let’s think now how to avoid coupling to the whole data structure. The answer is quite clear – use just data items instead. Doing so, we should end up with the loosest type of coupling that is data coupling.
Basically, it means that the recipient (method) takes a list of arguments (values). Notice that here we only pass values that are key to method execution. In other words, there is no space for any unnecessary items. For example, instead of passing the whole EmployeeDetails structure, we could pass just an employee’s id. In return, we also could get some single value, like a zip code.
Listing 4. Difference between stamp coupling and data coupling.
//Stamp coupling public EmployeeAddress findAddressFor(EmployeeData employeeData) { EmployeeAddress address = repository .findByEmployeeId(employeeData.getId()) //method body return address; } //Data coupling public ZipCode findZipCodeFor(EmployeeId employeeId) { EmployeeAddress address = repository .findByEmployeeId(employeeId) //method body return address.getZipCode(); }
We can say that a class/method is marked by data coupling when we can’t notice any other types of coupling there. Why did I even mention it? Let’s imagine a situation when we see a method that has a single parameter. We could potentially think that there is a stamp or data coupling, but it can turn out that actually the parameter is an element of control that is specific for control coupling. In such a situation, we should take a closer look at how a caller of the method sees it. If the caller uses a parameter as an element of control and expects some specific behavior by setting it then indeed we have a case of control coupling, however, if the caller passes the value and treats it just like any other data we are probably dealing with data coupling.
That’s it when it comes to types of coupling. I want to repeat here that this division into types is the original by Myers – the emphasis is put on communication between classes and the message itself. I’m mentioning it as there are also other types of coupling that are specific for OOP and those are probably the ones that we are most familiar with. What I mean by that is how classes are connected physically. Whether we create an object of some class directly, or use an interface, or, finally, if we may just emit some events without the knowledge who and how consumes them.
Myers defined types of coupling when procedural programming reigned supreme. I have an impression that then when OOP became popular, those types lost popularity and everybody’s attention focused only on those specific for OOP. Why do I think so? When we take a look at most code examples on the Internet looking for “examples of tight/loose coupling”, we mainly find code snippets that consider it only on a level of using interface (loose coupling) vs. specific implementation (tight coupling). But that’s a totally different story for another discussion or post ;).
Decoupling
Now that we know what types of coupling we can expect and how to identify them in our code, it would be good to find some ways of getting those types that are usually more desired. To do that we can use decoupling, which can be any technique that helps us to achieve more independent classes.
Each of the types suggests some forms of decoupling and in general it means favoring types with looser coupling. For example, if we identify a type of control coupling in a method and we can see two flows with different results based on the passed element of control, maybe we could split the method into two separate methods. Another example could be a situation when we see that a method has a parameter that is data structure and uses just a few fields from it. In this case, maybe we could replace the whole structure with a short list of parameters.
The next technique could be to design classes in such a way as though communication between them could be done using queues. In this approach, we focus on what a class should do and what it needs to execute its logic. Moreover, the moment of execution becomes irrelevant.
A good practice could also be to use “local” (in other words, context-specific) data structure in communication between classes. Thanks to that, we don’t allow them to spread all around the system but just to be used where they fit.
So that would be it for now when it comes to coupling. Last but not least I would like you to remember there is no black or white here. As it is quite common in our profession – it all depends – usually on the case or context. Sometimes we will need strong coupling and trying to lose it would not make any sense. The most important thing for us is knowing all those types and knowing the good, the bad, and the ugly sides to them.
Now it’s time to move on and take a closer look at cohesion if you haven’t done it yet.
P.S. If you have enjoyed this article and would like to always have it with you offline or you would like to have some super sketch notes with types of coupling and cohesion to print out and stick them above your bed, don’t hesitate and download an ebook, that I have for you. You will find there a few great posts about rotting design, coupling and cohesion put together into PDF, EPUB and MOBI formats.