Decouple your code

Decouple your code, your coworkers will thank you.

Decoupling isn't a new concept but I started documenting myself on it fairly recently. Here is what I learned.

What is "decoupling" ?

To understand decoupling you got to know what coupling code is first.

There is a coupling notion when 2 services with different responsibilities need one another to work. Those 2 services are "coupled".

Our way of dealing with classes will increase or decrease the coupling force:

  • a strong coupling is done when classes are initialized inside other classes for example. In general, using classes directly increase coupling.
  • a weak coupling exists when the first class doesn't know who is and how works the class she uses.

Example of coupled code :

<?php

use Config/IniConf;

class IndexController
{
    private $iniConf;

    public function __construct() 
    {
        $this->iniConf = new IniConf('../config.ini');
    }

    public function renderView()
    {
        echo '[' . $this->iniConf->getValue('db') . ' : ' . count($this->iniConf->getValue('db')) . ']';
        echo '[' . $this->iniConf->getValue('name') . ' : 0' . ']';
    }
}

My controller needs to access the app parameters that are stocked into the .ini file. No problem because I have a class that does just that : IniConf, so I use it. And I do it in several other controllers too: AboutController, AdminController, PortfolioController. Perfect.

But what if I decide tomorrow to change the address of my configuration file? Or if I happen to want to stock my parameters in a database for more flexibility? Do you see where this is heading? Each time, I need to change my controller(s).

The number of code lines I have to change can very quickly became extreme : I will be spending more time changing my controllers than writing the class that stocks the config in database.

"Decoupling" is the solution

How do you decouple code to limit this type of problem?

You can start by separating IniConf and IndexController. Of course IndexController needs IniConf (its dependency) to work properly but it shouldn't be the one initializing it. We will do  dependency injection:

<?php

// Constructeur IndexController
public function __contruct($iniConf) {
    this->iniConf = $iniConf;
}

There. This way, I can instantiate IniConf one time and pass it to the constructors of my controllers. It will limit the number of places I'll need to change the file's address.

For our problem with changing the whole method of parameters saving of the app, let's rewind a bit:

the first class doesn't who is how and how exactly works the class it uses.

It needs to know the type of class it needs and how it works. To do that, we will establish a contract between IndexController and IniConf.

Ways to do that vary depending on the language used : with PHP we will use interfaces, with Swift it will be protocoles.

<?php

// Contrat

namespace Config;

interface Configurator
{
    public function set($key, $value);
    public function get($key);
}

Whatever my parameter stocking method is, I need to be able to access the parameters and change the keys. You need to create an interface Configurator that does that.

<?php

use Config/Configurator;

class IndexController
{
    private $conf;

    public function __construct(Configurator $conf) 
    {
        $this->conf = $conf;
    }

    public function renderView()
    {
        echo '[' . $this->conf->get('db') . ' : ' . count($this->conf->get('db')) . ']';
        echo '[' . $this->conf->get('name') . ' : 0' . ']';
    }
}

I change the code of my controllers consequently :

  • The constructor asks an object that respect the contract Configurator
  • The controller uses the contract methods rather that the classes' methods.
  • The controller doesn't necessarily use IniConf, it doesn't know what it uses in fact, we change the attribute in $this->conf

The last thing to do is to change the code from IniConf to make it respect the contract Configurator.

Conclusion

Code is looking good, it is now decoupled. Nothing changes in the result, the new code does the exact same thing as the previous one but it is simpler to maintain.

My index.php code :

<?php

$conf = new IniConf('../config.ini');
$index = new IndexController($conf);
$other = new OtherController($conf);

$index->renderView();

The beauty of dependency injection.

I need to change the address of my configuration file:

<?php

$conf = new IniConf('../libs/config.ini');
$index = new IndexController($conf);
$other = new OtherController($conf);

$index->renderView();

The address is changed in a single place and benefits the entire app.

I migrate my configuration in database.

<?php

$conf = new BDDConf([
    'dbname' => 'config',
    'login' => 'dbuser',
    'passwd' => '1234'
]);
$index = new IndexController($conf);
$other = new OtherController($conf);

$index->renderView();

Change is transparent for my controllers, I don't change the code. I focused on my new feature (writing class for the new stocking method) and I didn't lose time changing every code line where IniConf appeared.

For those of you who would like to see it more in depth, here is the GitHub link of the project.