Emesary : Efficient inter-object communication using interfaces and inheritance

A technique that I have been using for a very long time to enable the inner workings of code to be cleaner and more decoupled, easier to maintain and extend.

In essence it is nothing that new - event driven systems have been around for a very long time.
What makes this way of doing things different is that it is very lightweight.

Why do I need inter object communications, I already have events from the window system

I've implemented this system a fair few times on many different systems, it is so lightweight and transparent that it doesn't need to affect the whole system.

This technique lets you safely connect disparate tiers in a managed and
predictable way.

Most of the time as the project progresses the rest of the team start to notice and really grasp what you can do with class based inter-object communication.

It differs from the native window events that you get in any windowing system (Win32, X, Qt) because it is always difficult and very unportable to manage and process your own events, often referred to as user events.

What you can't easily do with most of these schemes is to have localised notifications, to pass around objects in the message and to stop half-way through processing.

You can have a very localised messaging system (e.g. on a Form)
or something bigger that is used by a whole system

A worked example of the PostOffice notification system

If you take the simple case of a button to cancel an order. It is usual to have an event handler, often the IDE will provide you with the code and leave the cursor blinking. Start typing and add the code to tell the business object that the order has been cancelled and it's finished. Except that because the order has been cancelled the other buttons to confirm or modify the order should be disabled, and the button to create a new order needs to be enabled. Not a problem, just add the button_NAME->enabled calls.

However this is wrong - for a number of reasons. Firstly one button is controlling others, secondly the code to manage button states starts to become disparate and lastly it should be upto the business object to decide the actions that are applicable and the UI to take responsibility.

So what we need is Tier interoperation, where the business logic can join in with the UI, and tell it what the available options are. Some people argue that the business tier should know nothing about the UI, that it is essential to keep the UI completely seperated and this is a line that shouldn't be crossed.

Tier interoperation - or integration with backend business logic

Of course, that all sounds good in theory - keeping all of the tiers seperated, avoid call backs etc., but in reality it is often so very wrong.

The business logic often needs to be able to tell the UI something - maybe we add some methods, typically something like "order->is_Cancel_Available()" which returns true when an order can be cancelled, but wait - we've just effectively linked the business and UI tiers. Except that we haven't, because the UI doesn't have to call the function and because the business tier can ignore a cancel request - returning false or throwing an exception.

It's also getting worse now. We need to ask the business logic whether the options are available so that we can display the appropriate state. This means that it is possible, nay probably, that a call will be missed and in certain circumstances a button will be shown as enabled when it isn't. Usually messy rather than a big issue - at least in this simple example - but taking things further the consquences can be much worse.

So what we need is to link the business logic to the UI -
so that the business tier can tell the UI that an something has changed.
Using Emesary we can easily achieve this.

Does this allow queuing and asynchrous messaging?

No by design decision after long thought, discussions, peer review. Both queuing and asynchronous operations are something that break the underlying goals.

Firstly any message recipient can have one of four return states:

  • Success
  • Failure
  • Abort
  • Finished

In practice most of the time Success and Failure can be considered equivalent (more on that later) in that event nofication will continue. Abort and Finished are also closely equivalent in that event nofication will stop.

Can objects communicate across process boundaries

Easily - and still cheaply using shared memory, sockets. However this protocal isn't designed to be automatic, self registry and service discovery, so the processes need to have an arbitrated method of first establishing the connection. Once the connection is established a bridge between the PostOffices is built and event processing can continue.

Cross process boundary communication is simply and only requires two objects

Can objects communicate when located on different systems?

Easily and in much the same was as across process boundaries. although shared memory might not be an option.

Isn't it better to use CORBA, DCOM, RPC, SOAP etc?

For certain things it is much better - but much harder, and more prone to failure.

It tends to be much more under the developer's control - and equally not as good where information needs to be shared between disparate systems, where SOAP (etc.) would be better.

Conclusion

What I've presented here is the outline of a system that really works best within a process, or where a set of shared processes. It was never designed to replace complex mechanisms such as DCOM or CORBA.

Emesary Implementations

AttachmentSize
PHP emesary.php4.92 KB
zx_postoffice.h6.43 KB
C# .NET version : Emesary.cs2.95 KB