A New Approach To Inversion of Control With ProdigyView

Devin Dixon
Helium MVC
6 min readDec 16, 2018

--

We are taught when writing good software, to make the code reusable and modular. One of the most famous principles derived from this kind of thinking is the inversion of control, where the execution of the method is received from another piece of code.

Today inversion of control achieved mainly through the following design patterns: Factory, Service Locator, Dependency Injection, Strategy, and the Template Method. But there are other ways of achieving inversion of control that are not discussed, even though they meet the requirements for doing so. In this article, we are going to discuss a newer and unique way to achieve this concept.

How To Think About Control

To begin, we need to lay a foundation for the meaning of inversion of control (IoC). When breaking down control of execution, there are 4 different areas to consider. They are:

  • Control over the flow of an application
  • Control over an object’s creation
  • Control over dependent object creation
  • Control over binding

When control is inverted, it should be able to affect one or more of those areas within your code. Ideally, the inversion of control should consist of these 4 principles when implemented:

  • Decoupled the execution of a task from implementation.
  • Focus execution on the task it is designed for.
  • Free execution from assumptions about the system
  • Prevent side effects when taking control over execution

With the above understanding of what control means and how implementation should be thought about, we can now explore how ProdigyView implements its own Inversion of Control.

Both Static and Instance Approach

Before going into this article, it should be noted some of the examples below utilize a static approach. There are actually two classes: PVPatterns and PVStaticPatterns that give the developer the options on how they want to build their solution. For those that are initially against static, please read the Debunking The Myths of Static Classes, Methods and Variables.

Decoupling And Control Over Execution Flow

To have an inversion of control, the code needs to be designed to do just that, allow something else to take control. ProdigyView does this through an Adapter Like Pattern. Digging into the code, we can begin to explore this with PVSecurity::generateToken() function as an example:

ProdigyView starts off with what is called a base function. Its called a base function because it comes with the assumption that the execution behind the function will never be replaced for 99% of use cases, and thus this is the default execution. But what when we need to change execution? Notice the code has the following:

if (self::_hasAdapter(get_class(), __FUNCTION__))
return self::_callAdapter(get_class(), __FUNCTION__, $length);

This is our inversion for control over the flow of the application. The base function can be overridden with an adapter like so:

Thus when Security::generateToken() is called, it will instead use the new function (which in this example is inferior). The call to the function is tightly coupled but the execution is not.

In short, we have decoupled the execution of a task from implementation and taken control over the flow of the application. This is an inversion of control.

Focusing On The Task Designed For

When an inversion is implemented, it has to be targeted on where the control of execution is occurring. Referring to the above example and referencing this section:

if (self::_hasAdapter(get_class(), __FUNCTION__))
return self::_callAdapter(get_class(), __FUNCTION__, $length);

Going through the source code, the majority of functions have this kind of functionality which can be overridden with an anonymous function or subverting the call to another class. Each inversion is targeted like so:

Method::addAdapter(‘ClassName’, ‘methodName’).

Thus when designing a function to invert, we can have an extreme targeted focus on what the task was designed for. As a comparison, Dependency Injection focuses on having an inversion control for the entire class, the approach above has the ability to invert control at the method level.

Preventing Side Effects

An important factor to consider when inverting control is the prevention of side-effects. This happens when we have unknown dependences or have to modify the code directly — something IoC should NEVER do. For example, in our code if we write a class that multiplies a number:

<?php
$multiply = function ($a, $b) {
return $a * $b;
}
multiply($a, $b);
?>

And one day we have to make a change to the function like so:

<?php
$multiply = function ($a, $b, $c) {
return $a * $b+$c;
}
multiply($a, $b, $c);
?>

We have to go through our code and make changes to all the functions, we can very easily introduce side affections to the application. This is why we should NEVER directly modify the code and the reason why IoC exist. The adapter pattern allows the changing of code without modifying the code and avoiding side effects.

As for side-effect from dependencies, that depends on how the developer implements the function and how testing is applied.

Testing

When implemented, the adapter is easy to test. If its meant to replace ‘how’ and item is implemented, such as encryption/decryption, the test results should deliver the same result. If not, the problem is the code that is inverting control, not the methodology for doing so.

Referring to the examples above:

if (self::_hasAdapter(get_class(), __FUNCTION__))
return self::_callAdapter(get_class(), __FUNCTION__, $length);

Notice the variable $length. What is actually is passed through is any variable that the original function received and is piped to the function taking control without modification. Thus if there are any side effects, it will occur only due to erroneous coding in the class it was passed to, not because of changes made to the current application.

Control Over Dependent Object Creation

Another area of inversion of control is the ability to for facilitate independent object creation. In other words, the Hollywood rule of “Don’t Call Us, We Will Call You” approach. In ProdigyView, this is accomplished through observers, which are discussed here.

Another pattern that is extended from PVPatterns or PVStaticPatterns is the ability to implement observers. These are classes are listening for certain actions to be called. A quick coding example:

<?php
use prodigyview\design\InstanceObject;
//Have messenger extend PVObject
class Messenger {

use InstanceObject;
public function addMessage($message){

echo ‘<p>You have a new message: ‘.$message.’</p>’;

//Notifies that an event has occurred
$this->_notify(‘new_message’, $message);
}
}
?>

The notifier added here is called ‘new_message’. We can assign a class to listen for whenever the function is called. Now another developer can have dependent object creation attached like so:

<?phpclass SocialBook {

public function updateFeed($message) {
echo ‘<p>Someone is messenging you!: ‘.$message.’</p>’;
}

}//end class
$messenger->addObserver(‘new_message’ , ‘listening_closure’, function($message) {
$book = new SocialBook();
$book ->updateFeed($message);

}, array(‘type’ => ‘closure’));

Here we have a created a loosely coupled implementation that can extend the functionality of an application and provide control over dependent object creation. It is also free from any assumptions about the system and cannot cause side effects to the function that is calling it because it does not modify the code.

Control Over Binding

The area to focus on in the section is the control over binding. In programming, we often function and variables. Variables can include int, string, doubles, etc. Functions are methods that called. We can also have accesses to these elements based on their visibility public, protected and private. Binding is the process at runtime that associated components within a scope.

Moving away from PVPatterns in PVObject and PVStaticObject, ProdigyView allows for late dynamic binding with a class. As an example, we can start with an empty class like so:

<?php
use prodigyview\design\InstanceObject;
class EmptyObject{ use InstanceObject;}
?>

Following by the in the introduction of a class and a variable:

<?php
$empty = new EmptyObject();
$empty -> addMethod(‘open’, function(){
echo ‘<p>You have mail!</p>’;
});
$empty -> addToCollectionWithName(‘sally’, ‘Sally has sent you a message <br />’);?>

And when you call the method or access the variable:

$empty -> open();
echo $empty -> sally;

This is known as late binding and it falls under the control of the binding for a program. We have the ability to add and remove methods, which increases how we are able to create a loosely coupled architecture.

When To Use These Strategies

Inversion Control is strongly associated with Dependency Injection. The above illustrates how the Toolkit creates other kinds of inversion of control to help build dynamic applications. Now, these functions are not magic bullets for all, and can be used effectively under different circumstances. For examples:

  • With anonymous functions, adapters are best utilized when access to the scope is not required.
  • When adapters are piped to other classes, the state of the class should be taken into consideration.
  • Observers should be used when a response is not required from the application.
  • When using late static binding and without interfaces/abstract function, testing should be done before application to ensure the methods exist before application execution

Understanding the above methods can how you greatly expand your applications and also change the way you write your code. Successful code with this approach focuses on smaller and more targeted functional design than writing large functions.

--

--