Inversion of Inversion of Control

Inversion of Control is a common phenomenon that you come across when extending frameworks. Indeed it’s often seen as a defining characteristic of a framework. — Martin Fowler

The key part is ‘framework’ call you rather you call ‘framework’. The ‘f…


This content originally appeared on DEV Community and was authored by 3Shain

Inversion of Control is a common phenomenon that you come across when extending frameworks. Indeed it's often seen as a defining characteristic of a framework. -- Martin Fowler

The key part is 'framework' call you rather you call 'framework'. The 'framework' doesn't need to be any specific thing. An operating system, a language runtime, or an application framework could be that 'framework'. As you register an event handler (no matter explicit .addEventHander or implicit lifecycle), you are already using IoC.

But is IoC always good? Or we should say, is IoC always what we desired?

IMO most of IoC is not intended (DI is the one I think of so far), but the control is by nature inverted, especially in human-computer interaction programming, because this is how the computer works: it is the user controls. If user doesn't input then the program just keeps idling.

# The example provided in the blog above 
puts 'What is your name?'
name = gets
process_name(name)
puts 'What is your quest?'
quest = gets
process_quest(quest)

Then here comes a question: why does command line enquiry seem to keep the control? Isn't the thread blocking and waiting for a signal as well? That is true and in fact the computer is in certain degree event-driven, in form of interruptions. However, conceptually it is in control because it's an imperative process: statements executed in order. There is a clear flow of control.

And I think sometimes we need our control back. Like when there are callback hells. And luckily coroutine(async/await) is to rescue.

// in control

async function foo() {
  const x = await fetch('http://bar.com/');
  //...
}

// control inverted

function foo() {
  fetch('http://bar.com/').then(x=>{
    //...
  })
  //...
  // we get two flows of control now?
}

Maybe this example is so simple that you don't see any difference :/. So let's see another example: you are going to manage a WebSocket connection and it has some rules:

  1. when connected, server sends you 'LOGIN' and you should reply 'LOGIN:<auth token>' and finally when server replies 'VERIFIED' the connection is successful.
  2. whenever server sends 'PING', client must reply 'PONG' (in 30 seconds maybe) otherwise connection will be closed.

Normally WebSocket is event-driven and we might write a version like this

const socket = new WebSocket('foo.bar');
let isVerified = false;
socket.onopen = ()=> {
  socket.onmessage = ({data}) => {
    if(data=='LOGIN') {
      socket.send('LOGIN:'+authToken);
    }
    else if(data=='VERIFIED') {
      isVerified = true;
    }
    else if(data=='PING') {
      socket.send('PONG')
    } else {
      // process the data
    }
  }
}

This code might work. But there are edge cases like what if server doesn't reply the expected message? To add more state (isLogined, isSuccessful) is a solution but it's redundant (you might check current state whenever callback executed), and not easily-extendable if there are more steps.

However if the control is inverted, the logic will become much more natural. Imagine we have a IoCWebSocket and it provides a modified WebSocket with extra methods:

// resolve when it's open
async ready():Promise<void>;

// resolve when there is a incoming message
// reject when websocket error
async read():Promise<string>;

Then the logic becomes:

const socket = new IoCWebSocket('foo.bar');
// ...
// assume inside an `async` function body
await socket.ready();
let next = await socket.read();
if(next!='LOGIN') {
  throw Error('Unexpected reply '+next);
}
socket.send('LOGIN:'+authToken);
next = await socket.read();
if(next!='VERIFIED') {
  throw Error('Unexpected reply '+next)
}
while(true) {
  try {
    next = await socket.read();
    if(next=='PING') {
      socket.send('PONG')
    }
    else {
      // process the data
    }
  }
  catch {
    // may catch error and re-throw if it's not due to connection closed.
    break;
  }
}

Do you notice the order (which is an implicit rule) is naturally guaranteed?

And we may have many analogous situations like drag-n-drop, step-by-step enquiry, cheat codes, (stateful) animations and interactions(hold to activate/n-times click)......they are procedures with extra (temporary) context informations, they're designed to have an order, they could own their own flow of control.

Bonus: Co-routines as an alternative to state machines

This post is about 'what'. As for 'how', I'm still investigating the most optimal solutions. Promise and async/await are fine in most cases.


This content originally appeared on DEV Community and was authored by 3Shain


Print Share Comment Cite Upload Translate Updates
APA

3Shain | Sciencx (2021-07-24T15:32:08+00:00) Inversion of Inversion of Control. Retrieved from https://www.scien.cx/2021/07/24/inversion-of-inversion-of-control/

MLA
" » Inversion of Inversion of Control." 3Shain | Sciencx - Saturday July 24, 2021, https://www.scien.cx/2021/07/24/inversion-of-inversion-of-control/
HARVARD
3Shain | Sciencx Saturday July 24, 2021 » Inversion of Inversion of Control., viewed ,<https://www.scien.cx/2021/07/24/inversion-of-inversion-of-control/>
VANCOUVER
3Shain | Sciencx - » Inversion of Inversion of Control. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2021/07/24/inversion-of-inversion-of-control/
CHICAGO
" » Inversion of Inversion of Control." 3Shain | Sciencx - Accessed . https://www.scien.cx/2021/07/24/inversion-of-inversion-of-control/
IEEE
" » Inversion of Inversion of Control." 3Shain | Sciencx [Online]. Available: https://www.scien.cx/2021/07/24/inversion-of-inversion-of-control/. [Accessed: ]
rf:citation
» Inversion of Inversion of Control | 3Shain | Sciencx | https://www.scien.cx/2021/07/24/inversion-of-inversion-of-control/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.