The Open Closed Principle - SOLID

The open-closed principle states that classes must be open for extension, but closed to modification, in short you should build your classes in such a way that you never have to go back and alter old code when adding new functionality around it.∂

A Brief Explanation

Today we are looking at the open closed principle. This principle attempt to make you write code which is easier changed and modified at a later date by touching as little as possible.

You may know the common scenario where during a client meeting you will hear the dreaded sentence “I want it to work like this now”, and in these situation you may here the common developer excuse “It wasn’t built like that” or “Thats not the way its being developed”. What this really means is that you haven’t left a decent enough layer of extraction in your code which would allow you to easier chop and change functionality without touching large amounts of your code base and risk on introducing new bugs and errors.

In Practice

Take the example below, this is an app that searches for Artist album information

    //Discogs is a search API for bands and artists musical releases

class DiscogsSearch {

    public function setAccessToken()
    {   
      //
    }

    public function execute ()
    {
        return ['qqqq'];
    }
}

class SearchController {

    public function search() {

        $parameters = ['artistName' => 'Arctic Monkeys', 'albumName' => 'Humbug'];

         //Problems:
        // There is alot of extra lines of code here that we could move out into its own class
        // What is we at a later date want this route to use the LAstFM search API, rip it all out?
        // what if we want to query a completely new api on this route,, i will have to reqrite this class all again?

       $lastFmSearch = new LastFmSearch();
       $lastFmSearch->setAccessToken();
       $lastFmSearch->setParameters($parameters);
       $results = $lastFmSearch->execute();
        var_dump($results);
        }
}

So to improve this code we will need to abstract the search functionality out of the search controller and into its own class. We need the search function to be more generic, allowing us to stick other search services in there if needed without touching the existing code too much.

we will begin by creating an interface which all search classes will implement.

interface SearchInterface {

    public function setAccessToken();

    public function setParameters($parameters);

    public function execute();
}
```php

So as for the interface above, all search classes that implement this intreface will need to have the methods;

* setAccessToken
* setParameters
* execute

class DiscogsSearch implements SearchInterface {

  public function _construct()
  {
    // set up curl request
  }

  public function setAccessToken()
  {
    // Set accessToken to curl request
  }

  public function setParameters($params)
  {
    // set parameters on curl request
  }

  public function execute()
  {
    return ['qqqq'];
  }

}

class LastFmSearch implements SearchInterface {

    public function _construct()
    {
        // set up curl request
    }

    public function setAccessToken()
    {
        // Set accessToken to curl request
    }

    public function setParameters ($params)
    {
         // set parameters on curl request
    }

    public function execute ()
    {
        //execute curl request
        return 'search results from LAstFM artist API';
    }
}

Now we write a wrapper class, this will take any search service that extends SearchInterface, this will allow us to switch between our designed search providers easily

class SearchService {

  public static function search(SearchInterface $searchService, $params){

      $searchService->setAccessToken();
      $searchService->setParameters($params);
      return $searchService->execute();
  }

}

Now this wrapper class is used in the search controller. We have moved out all the classes setup functions, smaller controlle and we now have the abiltiy to chop and change the search service, making our code more scalable and adaptive.

class SearchController {

    public function search()
    {
        $parameters = ['artistName' => 'Arctic Monkeys', 'albumName' => 'Humbug'];

        $results = SearchService::search(new DiscogsSearch(), $parameters);

         var_dump($results);
    }
}

See how simple this is? If we want to switch the search provider to LastFM, or add a new service all together we simple need to;

  1. Make a new class
  2. Fulfill the contract in the interface (implement all required methods)
  3. Change the class that we are using in the UserController->search() method Easy!