A concept that comes up a lot while doing object-oriented design is coupling. We use it to describe how connected the different parts of your code are to one another. But, even explained like that, the concept of coupling can still be a hard to grasp.
On top of that, we often pair it with another concept called cohesion. We use cohesion to describe how well the different parts of your code fit together. This tells us whether everything was well-designed or not.
In fact, this is why coupling and cohesion are so important. There’s a strong relationship between them and the quality of your code. Code that doesn’t have any major coupling or cohesion problem is more often of higher quality. It’s more maintainable, reusable and less prone to problems.
That’s why you want to keep these two concepts in the back of your mind when programming. But that’s easier said than done when these concepts aren’t well understood. That’s why we’ll demystify them today.
Defining coupling and cohesion
The introduction only did a small overview of both concepts. But before we proceed any further, we need to take a longer look at both coupling and cohesion. Once we understand them better, we can look at how they relate to each other.
Coupling
If we could resume the concept of coupling to a single word, it would be “interdependence”. Coupling is a way to measure how parts of your code are dependent on other parts of your code. In object-oriented programming, coupling looks like this:
Each arrow in this diagram is a dependency that your class has with another class. The more dependencies a piece code has, the more coupled it is. And the more coupled a piece of code is, the more likely it is to break when you make changes.
That’s because your piece of code is vulnerable to changes from all its dependencies. Let’s imagine that you make a change to the C
class. Well, that change could break something in the A
class since it depends on the C
class.
Types of coupling
So far, we’ve described coupling as a dependence on another piece of code. In reality, coupling can be a dependence on anything. It’s about the external information that your code depends on to work.
If we take our earlier diagram as an example, it describes coupling in an object-oriented programming context. In that context, you can couple your code to classes and/or interfaces. It’s often the context where you hear about coupling for the first time.
That said, coupling isn’t something that you only see with object-oriented programming. For example, you can couple your code to data or global variables. (More on that later!) Those types of coupling don’t require object-oriented programming to happen.
There are also terms to describe these different types of coupling. But you won’t see them used much in practice. Instead, we tend to just say, “This code is coupled to X.”
How to describe coupling
What we tend to focus more on is the amount of coupling that a piece of code has. We use a scale to describe it. You can see a diagram of it below:
On one side, you have code that has a lot of coupling. On the other, you have code with little to no coupling. We use two terms to describe the two sides of this scale: tight and loose.
Tight coupling
When your code is tightly coupled, it means that it requires a lot of external information. In the coupling diagram earlier, most of the classes had dependencies on other classes. This created a lot of tight coupling because classes needed to know about other classes to work.
Tight coupling is something you want to avoid as much as possible. It’s the source of the code quality issues that we brought up earlier. It makes your code more brittle, harder to maintain and less reusable.
Loose coupling
On the opposing side, we have loose coupling. Loosely coupled code doesn’t need a lot of external information to do its job. Most of what it needs is already on hand.
The C
class was the most loosely coupled class in our earlier diagram. It had no dependencies whatsoever. When a piece of code has no need for any external information like that, we say that it’s fully decoupled or has no coupling.
It’s ok to have a bit of coupling
You shouldn’t make it your goal to make all your code fully decoupled. It’s a nice-to-have if you can do it, but it isn’t necessary. There are diminishing returns as you remove coupling from your code.
Removing all coupling from your code can be a lot of work. More often than not, it’s more work than maintaining it with a few dependencies. But how many dependencies should your code have?
The reality is that there are no set rules when it comes to this. There’s just no way to calculate the amount of dependencies that your code can or should have. It’s something that you have to keep evaluating all the time.
That’s because coupling will grow and evolve with your code. (It’s like weeds growing in your beautiful code garden!) The two are inseparable from one another. So you have to be vigilant about it.
As your code grows and evolves, you’ll need to check for new dependencies in it. And, when you find these new dependencies, you’ll have to determine if you need to remove them or not. This cycle will continue as long as you keep working on your code.
Cohesion
Now that we’re familiar with the concept of coupling, we can move on to its partner concept: cohesion! Like coupling, we can also resume cohesion to a single word: belonging. Cohesion measures how well various parts of your code belong together.
But what do we mean when we talk about various parts of your code belonging together? Well, let’s imagine that we have code that we’ve grouped together inside a class. (In theory, it could also be in something larger like a plugin.) Cohesion would measure how well this code works together as a cohesive (ha ha!) unit.
Cohesion is a lot like coupling in terms of how we describe it. Like coupling, there are different types of cohesion that we don’t use much in practice. Instead, we tend to describe the amount of cohesion in a block of code using the terms low and high.
Low cohesion
Let’s go back to our hypothetical class that we used to group code together. If we said that this class had a low cohesion, what would that mean? It would mean that the code that we grouped together inside it doesn’t work well as a unit.
class MyPlugin_Utilities { /** * Check if the plugin is active. * * @return bool */ public function is_plugin_active() { // ... } /** * Get the name of the web server running PHP. * * @return string */ public function get_web_server() { // ... } }
The MyPlugin_Utilities
class above is an example of a WordPress class with low cohesion. There’s no strong relationship between the is_plugin_active
and get_web_server
methods. We grouped them together because they didn’t quite fit anywhere else.
Classes with low cohesion suffer from similar issues as those with tight coupling. They’re often difficult to maintain, test and/or reuse. That’s because the low cohesion makes it hard to understand what those classes are doing. (The MyPlugin_Utilities
class name also reflects this difficulty in understanding what the class does.)
High cohesion
Meanwhile, a class with high cohesion is one where all the code inside it works well as a unit. A class with high cohesion often has the opposite properties as one with low cohesion. It’s more reliable as well as easier to test and reuse.
Not only that, but a class with high cohesion also tends to be smaller and more focused than one with low cohesion. This results in a class that’s less complex. And, in turn, this makes it easier to understand what the class does.
These are all desirable properties that we associate with high-quality code. They’re also the same properties as classes with loose coupling. But, while they share the same properties, they have different reasons for having them.
A self-reinforcing relationship
This brings us to the great mystery surrounding these two concepts: their relationship! Why are coupling and cohesion related to one another? It’s because both concepts are self-reinforcing.
The diagram above attempts to illustrate how that’s happening. As we saw earlier, coupling measures how much external information a class depends on to work. The less it needs, the better.
Now, when a class has few dependencies, it means that it has everything on hand to do its job. This increases the likelihood that the class has high cohesion. All the code inside works well as a unit and that’s why it doesn’t have a lot of dependencies. (And that’s how high cohesion promotes loose coupling.)
If we look at it from the opposite direction, we can see how tight coupling leads to low cohesion. The more dependencies a class has, the less likely it is that the class works well as a unit. So, in both directions, the relationship between coupling and cohesion is self-reinforcing.
Coupling in the wild
Now that we have a better understanding of coupling and cohesion, we can look at some examples. It’s worth pointing out again that coupling doesn’t only happen in object-oriented programming. Your code can depend on external information even in regular PHP functions.
Coupling to global variables
Due to its age and focus on backwards compatibility, WordPress still relies a lot on global variables. In fact, there’s even a codex page dedicated to all the global variables that WordPress uses. For example, you can only access the wpdb
class through the global variable that stores it.
/** * Search for posts with the given title. * * @param string $title * @param int $limit * * @return array|null */ function my_plugin_search_posts_by_title($title, $limit = 5) { global $wpdb; return $wpdb->get_results("SELECT * FROM {$wpdb->posts} WHERE post_title LIKE '%$title%' LIMIT $limit"); }
The my_plugin_search_posts_by_title
function above is an example of a function that uses the wpdb
global variable. But, by using it, it also became coupled to the wpdb
global variable. my_plugin_search_posts_by_title
cannot work without it.
This is the main reason why you shouldn’t use the global
keyword. When you use it, your code now depends on the global state to work. This almost always leads to serious code quality issues. (It’s also why it’s one of the worst forms of coupling.)
This code quality issue becomes obvious when you want to test the my_plugin_search_posts_by_title
function. Because the function uses the wpdb
global variable, there’s no way to isolate and test the code inside the function. This makes it harder to test because you must also ensure that the wpdb
global variable exists and works.
How to reduce coupling to global variables
Coupling to global variables is a problematic issue to deal with. it’s often a fundamental part of the design of an application. (For example, WordPress can’t work without its global variables.) This makes it a challenging exercise to code in a way that avoids coupling to global variables.
In practice, you can’t remove all coupling to global variables. That said, you can still mitigate the impact it has on your code. The primary way to do that is to limit the coupling to a single function.
/** * Get the global wpdb object. * * @return wpdb|WP_Error */ function my_plugin_get_wpdb() { global $wpdb; return $wpdb; } /** * Search for posts with the given title. * * @param string $title * @param int $limit * * @return array|null */ function my_plugin_search_posts_by_title($title, $limit = 5) { $wpdb = my_plugin_get_wpdb(); return $wpdb->get_results("SELECT * FROM {$wpdb->posts} WHERE post_title LIKE '%$title%' LIMIT $limit"); }
Here’s our previous example that we modified to use this strategy. We created the my_plugin_get_wpdb
helper function to fetch the global wpdb
object. It’s now the only function that depends on the wpdb
global variable.
We then replaced the global variable in the my_plugin_search_posts_by_title
function with a call to the my_plugin_get_wpdb
function. Because of this change, we can now test the my_plugin_search_posts_by_title
function. We can mock the my_plugin_get_wpdb
to return a mock of the wpdb
class.
Greedy coupling
Another way that we create coupling is by being greedy. In the context of coupling, being greedy is when we ask for more external information than we need. It’s often something that we do without intending to.
This is why the following example is interesting. It’s from another article on this site. (We all make mistakes!) It’s a great example of this greedy type of coupling. (The technical term for it is “stamp coupling“.)
class WPMemeShortcode_Shortcode { /** * @var WPMemeShortcode_Options */ private $options; /** * Constructor. * * @param WPMemeShortcode_Options $options */ public function __construct(WPMemeShortcode_Options $options) { $this->options = $options; } /** * Handles the output of the shortcode. * * @param array $attributes * @param string $content * * @return string */ public function handle(array $attributes, $content = null) { // Do nothing if no ID is given or it is not numeric if (!isset($attributes['id']) || !is_numeric($attributes['id'])) { return $content; } // If no size is given or it is not a numeric value, get default. if (!isset($attributes['size']) || !is_numeric($attributes['size'])) { $attributes['size'] = $this->options->get('size', '500'); } return "<img src=\"http://cdn.memegenerator.net/instances/{$attributes['size']}x/{$attributes['id']}.jpg\" />"; } }
Above, we have the WPMemeShortcode_Shortcode
class with a dependency on the WPMemeShortcode_Options
class. The problem is that we only use a single option from it: size
. We don’t use the class for anything else and that’s why it’s a greedy dependency.
Often, we create this type of coupling because we’re trying to protect our future selves. We expect that we’ll need more than one option in the future so we ask for the WPMemeShortcode_Options
class now. But all we did in practice is create coupling to meet a hypothetical need.
Removing greedy coupling
Fixing this type coupling is pretty straightforward. We know that we asked for too much information. So all that we need to do is change our dependencies to be less greedy.
class WPMemeShortcode_Shortcode { /** * @var int */ private $default_size; /** * Constructor. * * @param int $default_size */ public function __construct($default_size) { $this->default_size = $default_size; } /** * Handles the output of the shortcode. * * @param array $attributes * @param string $content * * @return string */ public function handle(array $attributes, $content = null) { // Do nothing if no ID is given or it is not numeric if (!isset($attributes['id']) || !is_numeric($attributes['id'])) { return $content; } // If no size is given or it is not a numeric value, get default. if (!isset($attributes['size']) || !is_numeric($attributes['size'])) { $attributes['size'] = $this->default_size; } return "<img src=\"http://cdn.memegenerator.net/instances/{$attributes['size']}x/{$attributes['id']}.jpg\" />"; } }
Here’s the fixed WPMemeShortcode_Shortcode
class. The dependency on the WPMemeShortcode_Options
class is gone. We replaced the options
internal variable and constructor parameter that we used for it.
Instead of the WPMemeShortcode_Options
class, we use an integer. It represents the default size of a meme generated by the shortcode. We store it in the default_size
internal variable.
Coupling through inheritance
One of the most important features of object-oriented programming is inheritance. It’s the feature that lets you create a class that extends
another class. When you create a class that way, it comes with a relationship to the class that it extends.
The diagram above illustrates this relationship. We have the derived class (also known as a child class or subclass). This class extends a base class (also known as parent class or superclass).
This relationship between the base class and the derived class is also a type of coupling. The derived class needs information from the base class to work. In fact, if the base class wasn’t designed with this relationship in mind, it can be a real problem.
In that situation, changes to the base class can break a derived class. When that happens, it means that there’s a tight coupling between the two classes. We call this form of tight coupling “subclass coupling”. The common consequence of it is something called the “fragile base class problem“.
Strengthening a base class
There are a couple of solutions to this coupling problem. One of them is just to trust your fellow developers to extend your classes as you intended them to. You’d want another developer to extend (ha!) the same courtesy.
That said, the world isn’t black and white. You might not want to rule with an iron fist. But you might still want to deal with this type of coupling in some situations. So let’s do that!
But first, let’s start by creating an imaginary base class. We’ll call it MyPlugin_Base
. (I know it’s quite the original name!)
class MyPlugin_Base { protected $variable; /** * Constructor. * * @param $variable */ public function __construct($variable) { $this->variable = filter_var($variable, FILTER_SANITIZE_ENCODED); } /** * Get the variable. * * @return mixed */ public function get_variable() { return $this->variable; } }
The MyPlugin_Base
has a constructor that accepts the variable
parameter. It sanitizes it using filter_var
before assigning it to the variable
internal variable. There’s also the get_variable
method that returns the value stored in variable
.
Now, this is just the MyPlugin_Base
class. Without a derived class extending it, it’s hard to know what problems the MyPlugin_Base
class has. So let’s look at derived classes that modify it in problematic ways and what we can do about it.
Securing a base class using encapsulation
class MyPlugin_Derived extends MyPlugin_Base { /** * Set the variable. * * @param $variable */ public function set_variable($variable) { $this->variable = $variable; } }
The MyPlugin_Derived
class shown above might look innocuous to you. After all, how much damage can the set_variable
method do? Well, a fair bit.
The set_variable
method doesn’t sanitize the new variable
that it gets. This breaks the expectation that the MyPlugin_Base
class has. It expects that it will always deal with a sanitized variable
. This can have some serious security consequences depending on what your code does.
So how do we fix this? One possible solution is to leverage encapsulation and method visibility. This lets the MyPlugin_Base
class control what variables and methods derived class has access to.
class MyPlugin_Base { private $variable; /** * Constructor. * * @param $variable */ public function __construct($variable) { $this->variable = filter_var($variable, FILTER_SANITIZE_ENCODED); } /** * Get the variable. * * @return mixed */ public function get_variable() { return $this->variable; } }
Above is our secured MyPlugin_Base
class. We achieved that by changing the visibility of the variable
internal variable. We switched it from protected
to private
.
Doing this prevents a developer from creating a method like set_variable
. There’s no way for the MyPlugin_Derived
class to access the variable
internal variable anymore. It can only get it through the get_variable
method.
Finalizing elements of a base class
Now, let’s say that we’re dealing with a clever developer. They really want to add that set_variable
method! They could create a MyPlugin_Derived
class like this:
class MyPlugin_Derived extends MyPlugin_Base { protected $derived_variable; /** * Constructor. * * @param $variable */ public function __construct($variable) { parent::__construct($variable); $this->derived_variable = $variable; } /** * Get the variable. * * @return mixed */ public function get_variable() { return $this->derived_variable; } /** * Set the variable. * * @param $variable */ public function set_variable($variable) { $this->derived_variable = $variable; } }
The MyPlugin_Derived
class now has a derived_variable
internal variable. The developer uses it to hijack the variable
internal variable in MyPlugin_Base
. They then create their own get_variable
and set_variable
methods using derived_variable
.
This is a more drastic (and maybe less realistic) example. But it’s always something that someone could do. (I might have done this a few times…) If you’re not careful, you can introduce a lot of bugs extending a class that way.
That said, as the developer of the MyPlugin_Base
class, what can you do? Well, you can use the final
keyword. This lets you prevent the MyPlugin_Derived
class from overriding elements of the MyPlugin_Base
class.
class MyPlugin_Base { private $variable; /** * Constructor. * * @param $variable */ public function __construct($variable) { $this->variable = filter_var($variable, FILTER_SANITIZE_ENCODED); } /** * Get the variable. * * @return mixed */ final public function get_variable() { return $this->variable; } }
Above, we modified the MyPlugin_Base
class and made the get_variable
method final. This blocks the MyPlugin_Derived
class from overriding the get_variable
method. And, as a result, the MyPlugin_Derived
class can’t hijack the variable
internal variable anymore.
Compromising with an interface
The solutions to strengthening a base class can be pretty extreme. (And they also tend to go against the spirit of open source.) There’s often no need to do something that drastic in practice. Well, that’s unless you’re trying to favour composition over inheritance.
That said, you might still want to reduce coupling through inheritance. But what can you do without resorting to the extreme options seen above? You could use an interface!
Creating an interface for our base class
Let’s go back to our MyPlugin_Base
class and create an interface for it. Keeping with our original naming scheme, we’ll call it MyPlugin_BaseInterface
. You can find the code for it below:
interface MyPlugin_BaseInterface { /** * Get the variable. * * @return mixed */ public function get_variable(); }
The MyPlugin_BaseInterface
has a single method: get_variable
. This is the method that we want the contract to enforce. Now, let’s have the MyPlugin_Base
implement it.
class MyPlugin_Base implements MyPlugin_BaseInterface { protected $variable; /** * Constructor. * * @param $variable */ public function __construct($variable) { $this->variable = filter_var($variable, FILTER_SANITIZE_ENCODED); } /** * Get the variable. * * @return mixed */ public function get_variable() { return $this->variable; } }
As you can see, we reverted the MyPlugin_Base
class to its original state. All that we did is add the implements
operator at the top. This formalizes the relationship between the MyPlugin_Base
class and the MyPlugin_BaseInterface
interface.
Coupling through type hinting
So why does an interface create less coupling than a class? A good way to show you why is using a feature of PHP called “type hinting“. It lets you enforce the type of a parameter that function or method accepts.
/** * Get the variable of a MyPlugin_Base object. * * @param MyPlugin_Base $base * * @return mixed */ function my_plugin_get_base_variable(MyPlugin_Base $base) { $variable = $base->get_variable(); // Do something with $variable return $variable; }
The my_plugin_get_base_variable
function above requires that the base
parameter be an instance of the MyPlugin_Base
class. The function uses the get_variable
method to get the variable stored in base
. It then makes changes to the variable
and returns it.
Now, type hinting is a good practice. It saves you the need to add guard clauses to your function. You’re always sure that base
is an instance of MyPlugin_base
.
Favour interfaces with type hinting
That said, we also created a pretty tight coupling between the two in the process. In reality, we only needed the type hint to use the get_variable
method. Were we greedy once again? (Uh oh!)
No, this isn’t greedy behaviour like we saw earlier. We didn’t ask for the MyPlugin_Base
class because we wanted more external information than we needed. We asked for it because it was the only way to ensure base
had the get_variable
method. We had no other choice.
But that’s why you should always try to use interfaces with type hints. An interface, like the MyPlugin_BaseInterface
interface we created earlier, is only a contract. It doesn’t contain any behaviour like the MyPlugin_Base
class does.
/** * Get the variable of a MyPlugin_Base object. * * @param MyPlugin_BaseInterface $base * * @return mixed */ function my_plugin_get_base_variable(MyPlugin_BaseInterface $base) { $variable = $base->get_variable(); // Do something with $variable return $variable; }
Here’s the updated my_plugin_get_base_variable
function. It hasn’t changed much from the previous version. We changed the type hint from MyPlugin_Base
to MyPlugin_BaseInterface
. That’s it.
But this small change is enough to reduce the coupling in the my_plugin_get_base_variable
function. Now, the function doesn’t need to always get an instance of MyPlugin_Base
. It just needs an object implementing the MyPlugin_BaseInterface
interface. This still ensures that the get_variable
method is there.
Cohesion and your class’s job
Wow, so we’ve talked a lot about coupling. But what about increasing cohesion? What can we do about that?
Well, increasing cohesion is a much more natural process than decreasing coupling. That’s why it doesn’t get talked about as much. With object-oriented programming, it all comes down to the job of a class.
This is something that comes up over and over in object-oriented design. You always want to ensure that a class has a job. This gives it purpose, but it also ensures that the code in it doesn’t have a low amount of cohesion.
But it’s also something that you have to keep reviewing. You code will evolve over time and a class can lose cohesion as a result. When that happens, there’s a good chance that it’s because your class now does too much.
The solution is to break up your class into separate classes. This should create classes with higher cohesion than the original class. Not only that, but your new classes should also have less coupling than the original class as well. That’s because we spread the dependencies throughout the new classes.
The never-ending battle
As you might have noticed by now, coupling and cohesion are huge topics. This only scratches the surface of it. There are design principles and philosophies based on these topics as well.
But what defines these two topics is their longevity. You’re never going to be done with them. The only way to be done with them is if you stop coding. (And we know you don’t want to!)
This is due to an idea called software entropy. It states the following:
- A computer program that is used will be modified.
- When a program is modified, its complexity will increase, provided that one does not actively work against this.
This means that, as your code matures, coupling and cohesion will grow in importance. You’re going to worry about them more and more. That’s why it’s crucial to know about it.