Removal Of Control in object oriented systems

Removal Of Control is a mechanism that will improve the usability, reliability and readability of code. I use commonly, it's similar in what I'm achieving but very different to IoC. The defining difference for me with ROC is:

Removal Of Control allows objects to be less tangled.

Introduction to ROC

Years ago I became frustrated with the amount of wasted effort when connecting code together. Sure if you're an electrician you've got to put those cables in, but I don't want to waste my time manually connecting everything together, and yet code need to communicate.

This isn't new and the idea / inspiration came from working with Event Driven systems, in my case GUI and others. Most GUI Event handling is appallingly badly designed and fragmented beyond belief because of the inherent complexity, and this is something that we must seek to avoid

ROC is a design based on a loosely coupled event driven methodology.

To give an example I'm processing orders in the backend and a possibly recoverable error occurs, the question is how to handle this, it's windows so I do the following in the backend. I know it's a bit nasty, but I can't do it with a return value, or an exception, and besides it'll not happen often so this is the easiest way.

class OrderProcess
{
...
    int  AddTransaction()
    {
...
        if (IDTRYAGAIN == MessageBox(
                NULL,
                (LPCWSTR)L"Transaction aborted\nDo you want to try again?",
                (LPCWSTR)L"Account Details",
                MB_ICONWARNING | MB_CANCELTRYCONTINUE | MB_DEFBUTTON2
                ))
        ...
    }
}

My order processing can now continue and retry the database operation. Trouble is that I've just linked my backend to the GUI directly. This is a dependency that I think everyone will agree needs to be removed.

So version 2 of the above is to provide an object to my database class to handle these errors, this is much better as I can inject this in during creation and do the following which is better isolated and at least we've got the direct dependency between the GUI and the backend removed which is a good thing.

class OrderProcess
{
...
    ErrorHandler errorHandler;
    void SetErrorHandler(GUIClass externalHandler)
    {
        this.errorHandler = externalHandler;
    }

    int  AddTransaction()
    {
        ...
        if (ErrorHandler.Retry ==
            this.errorHandler.NotifyFailure("Transaction aborted\nDo you want to try again?",
                                            "Account Details",
                                            ErrorHandler.Warning + ErrorHandler.Retry + ErrorHandler.Cancel)
        ...
    }
}

This still doesn't sit right with me though, it's very rigid and really the dependency has been simply moved, and the injection has become another objects responsibility.

The solution to dependencies

The solution is much simpler both in concept and implementation. The OrderProcessing code knows that a problem has occurred and that it needs to get some guidance from outside, so it asks for help. As it's inherited from GlobalRecipient the object has a Notify method available to call that will globally notify all recipients likewise inherited.

class OrderProcess : public GlobalRecipient
{
    int  AddTransaction()
    {
        ...
        MessageNotificationCard card("Transaction aborted\nDo you want to try again?",
                                     "Account Details",
                                      MessageNotificationCard.Warning + MessageNotificationCard.Retry + MessageNotificationCard.Cancel);

        if (Recipient.Abort == Notify(card))
        ...
    }
}

That's it - a few lines of code and the OrderProcessing module can do its job without any dependencies.

Obviously that's a ridiculously simple example and a lot of design complexity goes into the Notifications, but the principle remains the same for lots of things.

Embedding objects within notifications

This is getting to be the core of the advancement of ROC - the ability to notify objects of requests and embed another object that should help to service the request. Take the case of saving the open documents to disk. The objects that provide each individual view know what their data is. So when you hit the save button in the main window you don't want to be doing this

void save(FileStream f)
{
    view1.saveChanges(f);
    view2.saveChanges(f);
    view3.saveChanges(f);
...
}

It's horrible because the main window has to maintain a list of all the views that have been created. Any view created outside of the main view will need to be managed by the view that created it. This can lead to a lot of manually maintained wiring to achieve what is a fundamentally simple request. The solution is to decouple it thus

void save(FileStream f)
{
    Notify( SaveChangesNotificationCard(f) );
}

That's all there is too it. All objects that are inherited from, or registered with the GlobalRecipient (manager) will receive the card, and those that have data to save will handle it in their receiveNotification method

Underlying Principles to ROC

The underlying principles can be summarised.

  • Objects generally know what the need to get the job done.
  • Minimum external dependencies.
  • Minimising code distance.

Benefits

The biggest benefit is reusability. I know this is almost the holy grail of design and so many things claim that this concept or idea or framework or programming language will achieve reusablility.

Resuability claims are too often like washing powders that claim to wash whiter than white. They can't because it's impossible.

Even so, and given that only very little is truly ever truly reusable ROC is a very large step on the way to achieving well structured, efficient code, that is more likely to be reused.

Dependency Notification

Part of ROC is Dependency Notification. DN is an alternative to Dependency Injection. It needs more explanation, but it's basically intetwined with ROC and lets objects take notice of the elements that they need. The basic principle is that something important such as the main entry point will create something equally important such as a database connection. All of the underlying objects need this connection to work, so the the database connection is either a global, a class static, or via DI.

This whole approach still falls apart because there is too much wiring. So what we do is to package up a Notification that contains the database connection and send it off. Any object that needs to know about the database connection can then set itself up to use the connection.

The major benefit here is that this technique removes the need for any direct calls or links between objects. It means it's very easy to change a database connection and that only the objects that need to know about a database connection are actually going to process the notification. One section of code to handle it and it's job done already. This isn't about abstraction and layers, the dependency being notified could be a DAO object or it could be a generic connection that hides an ORM beneath a nicely abstracted interface. None of this actually matters to this concept, and it will work with any of it.

Summary

ROC is simple to understand, gives real benefits, and works in pretty much any objected oriented language that supports either interfaces or multiple inheritance.