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.
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.
Tightly coupled systems tend to exhibit the following developmental characteristics, which are often seen as disadvantages:
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 ™.
Loosely coupled systems tend to exhibit the following developmental characteristics, which are often seen as advantages:
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:
- Adding new data elements to messages
- Changing the order of data elements
- Changing the names of data elements
- Changing the structures of data elements
- 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:
$table_id
.Notice that this effect is achieved by splitting the original script into two separate parts:
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:
separate the concerns of constructing objects and using them. With my technique I am still instantiating a class into an object within the client which is then used within that client, and it is only the identify of that class which is supplied in a variable from an outside source.
should not have to know how to construct those services. With my technique the "how" is no more complicated than using the "new" keyword, or my separate singleton class. None of my table classes requires any external configuring as each has only a single configuration, and that is taken care of by standard code within the class constructor.
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 ...
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.
function _cm_whatever ($fieldarray) // interrupt standard processing with custom code { // customisable code goes here return $fieldarray; } // _cm_whateverEach of these methods is called by invariant methods within the abstract class, but by default they do absolutely nothing. All the developer has to do is copy the method into his concrete class and insert some customisable code, and at run time this will override the empty method in the abstract class.
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:
The developer will then instantly be able to visit that menu in order to select and run that transaction to view and maintain the contents of that table, all without having to write a single line of code - no PHP, no HTML and no SQL. The generated code can be customised, either by modifying the screen structure script to adjust the contents of the screen, or to modify and of the "hook" methods to insert custom business logic.
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: