Tony Marston's Blog About software development, PHP and OOP

The difference between Tight and Loose Coupling

Posted on 20th February 2025 by Tony Marston
Introduction
Coupling
Tight Coupling
Loose Coupling
The benefits of Loose Coupling
How to achieve Loose Coupling
A final bonus
References
Comments

Introduction

I have seen a large number of newbie PHP programmers ask the question What is this thing called "coupling"? What is the difference between "Tight" and "Loose" Coupling? What are the advantages or disadvantages of each?. While lots of people have given lots of answers I'm afraid that, in my humble opinion, those answers have tried to say the same thing using different words which can be interpreted in a multitude of different ways. Answers which are imprecise, ambiguous and open to interpretation are as useful as a chocolate teapot, so, in the interests of providing clarity, I shall now provide my own descriptions.


Coupling

As my software engineering efforts are limited to designing and building web-based database applications for businesses (also known as enterprise applications), with PHP as my programming language of choice, for the purposes of this discussion you may assume that for the term "module" I actually mean "object" (something which is instantiated from a class).

Coupling describes how modules interact. The degree of interdependence between software modules; a measure of how closely connected two routines or modules are; the strength of the relationships between two modules. It signifies a dependency between two modules.

Dependency is a state in which one object uses a function of another object. It is the degree that one component relies on another to perform its responsibilities. If ModuleA calls a method in ModuleB then those two modules are coupled. ModuleA depends on ModuleB while Module B is a dependent of ModuleA. ModuleB is not dependent on ModuleA as there is no call from B to A.

If two modules are coupled the strength of that coupling can range from low/loose/weak to high/tight/strong. Low coupling is better as it tends to create more methods which are polymorphic and therefore reusable via the mechanism known as Dependency Injection. Tight coupling does the opposite by reducing or even eliminating the number of methods which can be reused.

The RADICORE framework is an implementation of the Model-View-Controller design pattern, and the following examples show how a Controller communicates with a Model.


Tight Coupling

Tightly coupled systems tend to exhibit the following developmental characteristics, which are often seen as disadvantages:

  1. A change in one module usually forces a ripple effect or chain reaction of changes in other modules.
  2. Assembly of modules might require more effort and/or time due to the increased inter-module dependency.
  3. A particular module might be harder to reuse and/or test because dependent modules must be included.

In this wikipedia article there is a description of tight coupling:

Content coupling (high)

Content coupling (also known as Pathological coupling) occurs when one module modifies or relies on the internal workings of another module (e.g., accessing local data of another module). Therefore changing the way the second module produces data (location, type, timing) will lead to changing the dependent module.

Here is an example of tight coupling:

<?php 
$dbobject = new Person(); 
$dbobject->setUserID    ( $_POST['userID'   ); 
$dbobject->setEmail     ( $_POST['email'    ); 
$dbobject->setFirstname ( $_POST['firstname'); 
$dbobject->setLastname  ( $_POST['lastname' ); 
$dbobject->setAddress1  ( $_POST['address1' ); 
$dbobject->setAddress2  ( $_POST['address2' ); 
$dbobject->setCity      ( $_POST['city'     ); 
$dbobject->setProvince  ( $_POST['province' ); 
$dbobject->setCountry   ( $_POST['country'  ); 

if ($dbobject->insertPerson($db) !== true) { 
    // do error handling 
} 
?> 

An alternative to this would be to pass each column as a separate argument on the method call like in the following:

$result = $dbobject->insertPerson($_POST['userID'],
                                  $_POST['email'],
                                  $_POST['firstname'],
                                  $_POST['lastname'],
                                  $_POST['address1'],
                                  $_POST['address2'],
                                  $_POST['city'],
                                  $_POST['province'],
                                  $_POST['country'],
                                  );

The above example exhibits tight coupling because of the following:

For these reasons this particular Controller can only be coupled with the specified Model object. Likewise this Model can only ever be coupled with this particular Controller. There is no possibility of reuse, and this is a Bad Thing ™.


Loose Coupling

Loosely coupled systems tend to exhibit the following developmental characteristics, which are often seen as advantages:

  1. A change in one module does not usually force a ripple effect or chain reaction of changes in other modules.
  2. Assembly of modules usually does not require more effort and/or time due to the inter-module dependency.
  3. The inclusion of dependent modules does not usually make a particular module harder to reuse and/or test.

In this wikipedia article there is a description of loose coupling:

Message coupling (low)

This is the loosest type of coupling. It can be achieved by state decentralization (as in objects) and component communication is done via parameters or message passing.

In the same article it also states

Low coupling refers to a relationship in which one module interacts with another module through a simple and stable interface and does not need to be concerned with the other module's internal implementation.

In this wikipedia article it states:

The degree of loose coupling can be measured by noting the number of changes in data elements that could occur in the sending or receiving systems and determining if the computers would still continue communicating correctly. These changes include items such as:
  1. Adding new data elements to messages
  2. Changing the order of data elements
  3. Changing the names of data elements
  4. Changing the structures of data elements
  5. Omitting data elements

Here is an example of loose coupling:

<?php  // component script
$table_id = "person";                      // identify the Model
$screen   = 'person.detail.screen.inc';    // identify the View
require 'std.add1.inc';                    // activate the Controller
?> 

<?php  // page controller script for the ADD1 pattern
require_once 'classes/$table_id.class.inc';
$dbobject = new $table_id;
$result = $dbobject->insertRecord($_POST);
if ($dbobject->errors) {
    // do error handling 
}
?> 

The component scripts, page controllers and screen structure (View) scripts are described in more detail in RADICORE - A Development Infrastructure for PHP.

This has the following differences when compared with the tight coupling example:

Notice that this effect is achieved by splitting the original script into two separate parts:


The benefits of Loose Coupling

It is all well and good to say that Loose coupling is better than tight coupling, but without backing this up by explaining the benefits, and proving that you can see the benefits, you can be considered as nothing more than a snake oil salesman.

The benefits of loose coupling can be summed up in two words: Dependency Injection (DI) which is described as follows:

In software engineering, dependency injection is a programming technique in which an object or function receives other objects or functions that it requires, as opposed to creating them internally. Dependency injection aims to separate the concerns of constructing objects and using them, leading to loosely coupled programs. The pattern ensures that an object or function that wants to use a given service should not have to know how to construct those services. Instead, the receiving "client" (object or function) is provided with its dependencies by external code (an "injector"), which it is not aware of

This description was derived from how DI was implemented in the early OO languages, but a more modern language, such as PHP, has fewer artificial restrictions and a greater degree of flexibility. That, coupled with my own ingenuity, allowed me to devise my own unique implementation of DI, which goes as follows:

The statement that Dependency Injection leads to loosely coupled programs is completely wrong. DI does not lead to loosely coupled programs, it follows after modules have been created specifically with loose coupling in mind. DI is impossible without polymorphism which relies on multiple classes sharing identical method signatures. It is more correct to say that Loose coupling leads to dependency injection by following this logical path:

It should be obvious that if different objects do similar things but with different method signatures due to tight coupling that there is no polymorphism and therefore no way to inject any dependencies. Quod Erat Demonstrandum.

But how exactly does dependency injection provide something of actual benefit? The answer is: reusability. By providing more modules which can be reused instead of being duplicated it means that you have fewer modules to write, and the less code you have to write to get the job done the quicker that you can get the job done. For example, in an application built using the MVC design pattern in the traditional way every Model requires its own hand-built Controller due to the unique method names - especially the setters and setters for the individual columns - that exist with each Model. In my current ERP application there are over 400 database tables, each of which has its own class, but instead of 400 Controllers which have to be built by hand I have just 45 reusable Controllers which are built into the framework, one for each of my Transaction Patterns. While some programmers still produce a Controller which is responsible for all the use cases which can be applied to their Model - which in my opinion would violate the Single Responsibility Principle - each of my Controllers handles only a single use case. Each of these Patterns performs one or more CRUD operations on one or more tables, but without knowing the identity of these tables. Because of the way I have implemented loose coupling any one of these Controllers can be made to work with any Model, and I use DI - in the form of my component scripts - to identify all the dependent objects required that that Controller.

My ERP application has over 400 database tables and over 4,000 user transactions (use cases), and these are all provided by a library of just 45 Page Controllers. Is there any other framework that can match this level of reusability? Answers on a postcard to ...


How to achieve Loose Coupling

I was able to do this splitting because I ignored common practice in favor of what I considered to be "better" practices which produce code which provides more reusability and therefore more in line with the objectives of OOP. If I have to ignore someone else's idea of "best practice" because it does not produce the best results then I will do so, and anyone who dares to tell me that I am wrong will get the sharp end of my tongue.


A final bonus

While building new user transactions (use cases) using all these reusable components I began to see a lot of steps which were repetitive. In other words I saw repeating patterns. This vision enabled me to take the next step which was to write a program to perform all the steps for me as I was becoming too lazy to do them all myself. I based my design on the Application Model which I had encountered in UNIFACE (one of my previous languages), but instead of defining all the table structures in the Application Model first and then exporting them to create the physical database I decided to reverse it by building the application database first and then importing the details into a new Data Dictionary subsystem. I added functions in this subsystem to do the following:


References

The following articles describe aspects of my framework:

The following articles express my heretical views on the topic of OOP:

These are reasons why I consider some ideas on how to do OOP "properly" to be complete rubbish:

Here are my views on changes to the PHP language and Backwards Compatibility:

The following are responses to criticisms of my methods:

Here are some miscellaneous articles:


counter