Note this is not an Orleans post, not exactly — it’s just something I wanted to enhance on my Orleans Project, prior to moving on to demonstrating even more Orleans features! (Also, I need to learn more Orleans features to demonstrate!)
Prior to continuing with my Microsoft Orleans series, I wanted to make the Console app calling my Orleans Grains a bit simpler to use under the various examples. How can I do that? Polymorphism to the rescue!
In the previous few Orleans posts (links at bottom) I was swapping in/out calls to various Orleans methods in my Console app’s Program.Main.
That looked something like this:
In the above, as I added new Orleans functionality, I would simply tack on additional calls and/or comment out previously used functions. I’m not sure how far I will go with these Orleans examples, but since this is going on my third or fourth sample, I wanted a better way to manage these calls. In addition, give the user of the console application the option of choosing which function to play with, without having to require them to change code in between runs.
How can we do this? With a few simple steps:
- Add a “menu” system to the console application
- Have the menu system content consist of Orleans functionality that’s been implemented (The different Orleans features I’ve demonstrated thus far).
We’ve touched on polymorphism before, though perhaps not called it by its name. From Wikipedia:
What does this mean? Basically — use interfaces and/or other abstractions (like abstract classes). How does it fit in with what I’m trying to accomplish? Well, I’m implementing a bunch of different examples that call into a Orleans cluster to demonstrate a feature of Orleans. So what kind of interface definition could I use? Let’s start with:
In the above we have a method that does nothing but
PerformFunction (whatever that ends up meaning), and returns a task.
Hmm, one other thing that’s going to be needed is to use a
IClusterClient. There are a few ways I could accomplish this, keep it separate from the abstraction, and make it available to the implementing class, or just make it a parameter of the interface method. In the way I’m going to use the implemented classes later, I’m opting for making the
IClusterClient a parameter on the interface method. Updated to look like:
One final thing we’ll need (at least for now), is some method of describing the
IOrleansFunction. We can do that with this simple addition:
Now, we can provide a description for each implementation — something we’ll be using as the “choices” within the console app menu.
So why go through all this trouble? Prior to going this route, I had a harder time implementing new functionality, without having to “move around a bunch of stuff” and continual editing of already existing classes. How can I avoid this now? Well, the only thing I need to do now (ideally, if I did this right), would be add a new implementation of
IOrleansFunction and the system would pick it up without any fuss.
Why is polymorphism the neat? Because you can interact with interface methods without caring about the actual implementation. This makes your code more loosely coupled, and things like unit testing, and code maintainability are simpler; this is touched on a few posts elsewhere I’ve done (links at bottom).
I’ve done a few separate Orleans examples:
- Hello World
- Multiple Instantiations
- Stateful Grains
Those seem perfect for new implementations
This is going to mostly be copy/pasting from previous posts, just into their new abstraction, and providing a description.
Now that our implementations of
IOrleansFunction are complete, we simply need to plug them into our to be created menu system!
With the menu system, the user should be able to enter a number that corresponds to a specific Orleans feature, then that feature should execute.
A few things we’ll need for the menu:
- A collection of Orleans Features to list
- A way to display the features
- A way to execute a feature
- A way for the user to try multiple features, and exit from the menu
We’ll need to keep a collection of our
OrleansFeatures and what better way to do that than with another interface! I’ve defined a new interface
The above interface does nothing but return a
IList<IOrleansFunctions> when invoked.
The concretion of said interface is simple enough as well:
In the above, we’re just newing up and returning a list of each one of our current
OrleansFunctions that we created earlier.
Now we need a way to display features on our menu — luckily for us we thought ahead, and created a Description on our
That coupled along with our
IOrleansFeatureProvider, means we can enumerate what the provider returns us, printing out each description. We’ll be using a [slightly hacky(?)] method of assigning a feature to a number via the collection’s index, but /shrug, that’s ok right?
Let’s start a new class
In the above, we’re using the results provided by our
IOrleansFunctionProvider, enumerating them, printing out the
IList<IOrleansFunction> index along with the
IOrleansFunction.Description, parsing the user input, and attempting to invoke the appropriate method on the collection as per the index. Notice how we are only working with interfaces here as it pertains to
IOrleansFunction. This class has no idea what the implementors are, because it doesn’t really matter as to the scope of this class (loose coupling).
It’s a simple matter of adding an if conditional to check for a specific entry to exit the menu, as currently, our menu will loop indefinitely.
Add a new const to the class:
and a new conditional within the while loop to check for that escape string, prior to executing the grain sample:
The final piece for completing our menu, is to plug the new bits into the Program.Main of the application.
That’s simple enough to do, because it’s mostly deleting of code! I love deleting code!
Damn… that’s a lot. But all that code now becomes…
That’s a lot of removed code! (Granted, a lot of that code was refactored into the individual
IOrleansFunctions. But we never have to look at all that code again!
So what does it all look like?
Choosing the “Hello World” example (choice 0):
Choosing the stateful grains example (choice 2):
Exiting (choice -1):
This ended up being a longer post than I intended, but hopefully it will help convey how working with interfaces can help better abstract and break down the work you need to do. That coupled with other “features” of abstraction such as unit testing, loose coupling, and an easier “high level view” of an applications architecture, are why I enjoy working on abstractions so much.
Code for this post can be found https://github.com/Kritner-Blogs/OrleansGettingStarted/releases/tag/v0.30
- Getting Started with Microsoft Orleans
- Microsoft Orleans — Reusing Grains and Grain State
- GitHub Code as of post — https://github.com/Kritner-Blogs/OrleansGettingStarted/releases/tag/v0.30
- It’s all about the abstractions baby
- What is the Business Value of Unit Testing?
- Getting Started with Unit Testing and Moq