Active Object Design Pattern in Delphi (Part 1): Method Requests
In this blog post I will start to describe the process to develop a solution in Delphi using the Active Object Design Pattern. This pattern is a concurrency design pattern based in part on the Proxy pattern.
As far as I can determine it originated from “Pattern-Oriented Software Architecture, Volume 2: Patterns for Concurrent and Networked Objects” and you can view an updated paper on the subject here: http://www.dre.vanderbilt.edu/~schmidt/PDF/Act-Obj.pdf. I will refer to the paper from time to time and it may be good to first read it to grasp what it entails.
The Active Object Pattern describes a system where the Client interacts with a Proxy that directs commands to a Servant Object. The Proxy presents the same or similar interface as the Servant to consumers. The Proxy uses a Scheduler (or Activation Queue) to queue method requests to the Servant and dispatches them in order*. Methods with return values are returned as Futures (also known as Promises). Demanding a Future Value blocks until the value is ready.
Every call to the Active Object gets turned into a Method Request. So let us start there
The method request just like we saw in Lavender and Schmidt’s paper has a guard that determines if the call can be made and a call to invoke the actual method on the client. The Scheduler loops through the queued requests, checks their Guard and then issues a Call.
Some method requests need to satisfy Futures or Promised Values. Instead of the standard property “getters” in the Servant, the Proxy should generally return the value in a Future, so that the client can delay the use of the value for as long as possible since it may be a blocking action if the value is not ready. To facilitate with the return of Future Values consider this simple generic interface
Note: Delphi has a IFuture
I cover Future values in more depth in Part 3: Futures.
Direct conversion of Sample
In the sample presented in the paper each method request descends from the abstract method request and are constructed using the Servant and - in cases returning a value - a Future or Promise that needs to be satisfied. I recreated a skeletal structure representing the design of the sample in the paper below. The main difference here is that the Future is created with a Method Request. In the paper the Proxy created the Future and passed it into the Method Request to be filled.
And the Proxy would use the objects as follows
Generalizing the Sample
While this somewhat direct conversion of the sample from the paper would work in Delphi there are a few problems with the design. First, the sample is very simple. Only two method invocations with one type passed. There is only on Future Value type and lastly the Method requests are tightly bound to the Passive objects on which they operate.
We can use anonymous functions and closures to generalize the Method Request class
We can pass a simple TProc
that is invoked in TMethodRequest.call
via the Scheduler. We can also pass a Guard that can determine if the call can be made or needs to wait.
We no longer need to subclass TMethodRequest
, but how we return Futures? First, let us define the implementation of of our TFutureValue<T>
generic class:
Our code on the Proxy side is a bit more code, but it is still better than creating a new MethodRequest sub-class
The above looks like more work, but consider the case where the Servant had many more methods or many more properties. We would have to create classes for each of those. Say my Servant object has another property PropA of type integer we can easily and quickly add a setter and getter to the Proxy.
You will notice that Futures are now created by the Proxy as described in the paper. The closure we formed with the anonymous procedure now allows the scheduler to have no knowledge of Futures.
Why did I not simply use Tasks and Futures from the System.Threading library? You can create a Task without running it immediately and with a bit of effort you can create a Future that is not started immediately so perhaps I could have run those as the scheduler dequeues them. I initially went down that path, but the code was actually more complex and more unreadible. I needed a simple solution that checks a guard and calls a method. While most methods do not need guards, it is part of the pattern and was hard to implement with the out-of-the-box structures
You can download the source code. Please comment or contribute.
Leave a Comment