Examples of Inversion of Control

In the post Inversion of Control (IoC), I described what was. Here I describe several examples of Inversion of Control.

Dependency Injection

When creating a class or module, it is good practice to reduce concrete dependencies when possible (Dependency Inversion Principle) . Concrete means having a hard dependency on another piece of code rather than a dependency on an abstraction.

This can be illustrated by defining a class. For example, a BillingForm may depend on a PaymentProcessor:

class BillingForm {
  constructor() {
    this.processor = new PaymentProcessor();
  }
  processForm(...) {
    this.processor.process(...);
  }
}

Contrast above with the following example where the dependency is added as an argument when constructing a BillingForm instance.

class BillingForm {
  constructor(paymentProcessor) {
    this.processor = paymentProcessor;
  }
  processForm(...) {
    this.processor.process(...);
  }
}
const paymentProcessor = new PaymentProcessor();
const billingForm = new BillingForm(paymentProcessor);

This is an example of IoC because the control of when a dependency is added is controlled by when the BillingForm is instantiated rather than immediately during the BillingForm instantiation.

React Lifecycle Methods

The Javascript frontend framework React.js has component lifecycle methods for components. Examples like componentWillMount, componentDidMount, and componentWillUnmount are defined in a component. These methods can be overridden by a component. The methods are then called at the appropriate time by the React.js framework.

Other examples includes: Command Line Interface, Javascript callbacks, Route Handlers, Object oriented class frameworks, and User Interfaces

Inversion of Control

Inversion of Control (IoC) is usually foundin programming, but it is a general concept. The concept relates to a program’s flow of control.

A program’s flow of control can be thought of as the direction a program executes. The simplest illustration of this is top-to-bottom line-by-line execution of a program.

console.log(1); 
console.log(2); 
console.log(3);

The numbers 1, 2, 3 will be logged into the console in this order. The written program has control of what and when the numbers are shown.

Contrast the above example with:

document.querySelector('.button-1')
.addEventListener('click', function(e) {
  console.log(1);
});
document.querySelector('.button-2')
.addEventListener('click', function(e) {
  console.log(2);
});

This code add event listeners to 2 buttons button-1 and button-2. The program describes what will happen (logging a number), but it has left when to the browser which in turn leaves it to the user of the browser. It is said that the control has been inverted away from the application/program (the code which defines the number logging) to the framework (the browser)

The word “framework” is an abstract and necessary concept when dealing with IoC since it would decide when certain methods shall be called.

To read more, check out Examples of Inversion of Control

Delta propagation’s view-update problem

In reactive programming, the “push” implementation can take several forms of one node (computation) passing along a directed edge (a path from one node to another) some change information to another node. Two forms of change information that can be passed is:

  1. Complete node state
  2. Delta propagation

A node could pass the complete node state to the next node. The node receiving the complete state of the previous node could then use this information to compute its own new state. This is a straight-forward implementation, but it can be inefficient if the next node doesn’t need the complete state of a previous node to compute its own state.

To optimize, one could use delta propagation which only passes along the changed data of a node. This would be enough for the next node to compute its new state depending on the exact implementation.

The view-update problem occurs when a program needs to maintain the state of nodes while updating their states upon an update in data. Updates in data should only affect nodes where there is a path from the node which has a change in its data and connected nodes. In a directed acyclic graph with nodes connected with directed edges, the view of nodes passed the node with the change should be updated.

Here is an example:
node A -> node B
node B -> node C
node B -> node D

If there was an update in node B, then node views for C and D should be updated 1

The view-update problem is often described within the context of databases since relational database management system maintain different levels of views of its data. There is the external level where an end-user of a database would interact with to get data. There is the lowest level where the actual data is stored on physical hardware (disks, tape, SSDs). In between these two levels is the conceptual level which is relevant to the database software coordinating the two levels.

Each of these levels maintain their own view of the data. The external level is more interested in the data and less interested in how the data is stored on the hardware. This results in the database view-update problem. The hardware could be optimized by creating better indexes of the data or by replacing it with a faster technology. The external level should not need to know of this hardware update to generate its own view of the data.


1: For the sake of example, node A does not need to be the cause of the change in node B.

 

Transitive closure

Apart of learning more about Reactive programming, I came across the term Transitive Closure which has roots in Mathematics. Below I set out to decompose the concept into smaller concepts to clarify understanding.


A “set” is a strict list of elements. For example, set X can be a list of numbers {1,2,3}. An element, or number in this case, is said to be a member of the set X if it is found in set X. The number 1 has membership in set X.

Let’s also define another set Y to be {4,5,6}.

2 elements can be paired together to represent a list. The numbers 1 and 2 is a list of 2 elements or a pair. In addition to defining what elements are included and how many elements are in the list, the order can be specified. We can order 1 to be first and 2 second, or we can order 2 to be first and 1 second. In Mathematics, this list usually is written via a tuple, more specifically a 2-tuple to specify that there are only 2 elements, by arranging the elements in a specific order within "( )" Parenthesis. The tuple is a limited or finite ordered list able to contain an arbitrary number of elements. The lists described above can be written as two 2-tuples or ordered pairs; (1,2) and (2,1).

Multiple ordered pairs can be grouped in a relation which represents elements within an ordered pair are related. In this example, the specific relation is a binary or 2-place relation. The word “binary” indicates or involves the quantity of two. This is typically found in Computer Science to mean the Binary numbers 0 and 1; On or Off.

Here is a binary relation for the sets X and Y denoted by the letter R.
R = {(1,4), (1,5), (5,1)}
1 is related to 4“. “1 is related to 5“. “5 is related to 1“.

Notice there are only 3 ordered pairs in this binary relation. This is a subset of the Cartesian product of the sets X and Y. The Cartesian product is a set of all combinations of all the elements in the sets. The full Cartesian products of sets X and Y is

X * Y = {1,2,3} * {4,5,6} = {(1,4), (1,5), (1,6), (2,4), (2,5), (2,6), (3,4), (3,5), (3,6)}
Y * X = {4,5,6} * {1,2,3} = {(4,1), (4,2), (4,3), (5,1), (5,2), (5,3), (6,1), (6,2), (6,3)}

With the ordered pairs and binary relation defined, the word transitive can be explained. The transitive relation means by knowing the two relationships of some elements, a third relationship can be established. If we said a is related to b and b is related to c, then the relationship a is related to c can be established.

A more concrete example can be the following. John is friends with Jane. Jane is friends with Fred. There is a transitive relation between John and Friend since they both share the friend Jane in common.

The second word in Transitive Closure is Closure. In Mathematics, a set can be said to be closed or exhibit the Closure property when some operation or procedure done on a set only strictly outputs members already in the set. In the context of sets and binary relations, the relevant operation or procedure can be finding the relations of elements in a set. 

Let’s now pull all of this together along with continuing with the previously defined set X, set Y, and the binary relation R.

set X = {1,2,3}
set Y = {4,5,6}
R = {(1,4), (1,5), (5,1)}

We see “1 is related to 4“. “1 is related to 5“. “5 is related to 1“. Looking at the two relationships “5 is related to 1” and “1 is related to 4“, we can establish a new transitive relation “5 is related to 4“. No other transitive relations can be established. This new pair (5,4) can be added to the ordered pairs in R. Since R is a strict set, we’d have to define a new set which includes the new ordered pair. The new set can be written as

R' = {(1,4), (1,5), (5,1), (5,4)}.

Since no other ordered pairs are necessary to represent all the relations of the numbers in the original R set, we have found the transitive closure of the binary relation R.

The formal definition of a transitive closure is:

In mathematics, the transitive closure of a binary relation R on a set X is the smallest relation on X that contains R and is transitive. Wikipedia


This was an explanation of the concept Transitive Closure using underlying concepts.

For more resources, I found this video helpful in understanding Transitive Closures since it explains the concept further with a graph: Transitive Closure Video.

Let me know your thoughts or whether there are any inaccuracies in the explanation.