Navigate Into Success: I had a dream: codeunit references
Polymorphism is a great thing. .NET, Java, C++ and other folks have enjoyed its benefits for a long time, some more than a quarter century already. Meanwhile, we, the meager C/AL bunch, can only dream about it and draft our little patterns that all come short of it if only by a tiny bit.
Sometimes it feels like writing code with a chalk on a blackboard. There may be no end to your imagination, but there is a very real end to what the board can do for you.
But dream we can. And let me have another dream.
Why do I care so much about polymorphism, what’s so important in it that I’ve spent three blog posts talking about it, and then am dreaming out loud another one, only before I write a yet another one?
Well, in all honesty, I couldn’t care less about polymorphism itself. It’s just another programming concept. What I do care about is the things it can do for us.
In the old days, when there was on-prem only, even if polymorphism existed, it would not have been more than a architectural stunt, a trick to write our CASEs and IFs in a more elegant way.
But with Dynamics 365, with apps, and even only with extensions running in multi-tenant scenarios self-hosted by partners, polymorphism can achieve far more than offer some fun to programmers.
How’s that? Well, consider the past three post I wrote and the problem that we find buried deep down, every single time, no matter what angle we take, how far we dig, or how clever we attempt to be: either we have a sort-of-a-polymorphism at the cost of statelessness (and possibly some more); or we have statefulness at the cost of tight coupling on the far end of the façade in charge of loose coupling. No matter what you do, no matter how you approach it, this is what you get in the end.
However, one object type, which wouldn’t be that crazy difficult to add to the C/AL stack, would solve it all: CodeunitRef.
What would a CodeunitRef be? Well, think of RecordRef. You know what it is? Well, a CodeunitRef is the same, only for codeunits.
Consider this piece of code:
You may recognize the “handled” façade codeunit from the previous post. It retrieves the codeunit ID for the codeunit that should be in charge of handling the event, and then passes that codeunit ID on to the event, hoping that there is a codeunit with that ID that will actually decide to execute the logic, and that there are no more codeunits that would feign to be the one being called, and then hoping that whoever decides to handle the event will set the Handled flag to true. Far too much hoping for my taste.
We could solve that in one fell swoop if only we could manually bind codeunit’s subscriptions, but to do that, we need an actual variable. The only way to solve it would be this:
This achieves the goal of having only the exact codeunit bound to the events, making sure only that codeunit executes the needed event. You also make sure that the bound codeunit retains its state (provided that somehow the façade codeunit does not go out of scope itself, losing the state – which can be solved in may different ways). You also achieve good separation of concerns: the actual event logger being called is only concerned with the business logic and doesn’t need to care about infrastructure; it’s the façade codeunit that takes care of it. There is no need to pass the identifier or the Handled flag into the event, because you know that there is only the correct handler bound to the event, and that it will execute.
However, you also provide hard coupling between the façade and the actual dependencies. This is bad because it requires changing code in and recompiling of the façade codeunit every time a new dependency type is added (say: Twitter logger codeunit). And every time you change and recompile the façade, for good measure you must at least recompile all the consumers, just to make sure you didn’t break anything.
With true loose coupling, or true polymorphism, you could add dependencies to the end of the world, without the need to touch or recompile anything.
But, how do you like this:
I myself totally dig it, except for a little detail: it’s completely made up. It’s impossible.
What would make it possible is the CodeunitRef type in C/AL, variable EventHandlerRef being of CodeunitRef type.
As long as I maintain a living reference to the instance of the codeunit inside of this variable, that instance would be both alive and accessible, meaning that I would now be able to both retain the state, and access the codeunit without the need for a codeunit variable with an exact subtype. CodeunitRef variable would not have subtype: it would be able to change its subtype at runtime, much like RecordRef can do (okay, it doesn’t actually change subtype, but you know what I mean).
Much like we have FieldRef when handling RecordRef, we could also have FunctionRef when handling CodeunitRef. This is what, in fact, other languages call delegates.
… and then this:
Once you have it, when you need to log events using whatever dependency is configured to handle it, you do this:
This stuff above, that I have just shown, is true inversion of control pattern executed using dependency injection (dependency injection is a manifestation pattern of the inversion of control pattern).
There is no end to what this could achieve. Maybe you don’t see it from this simple blog post, but dependency injection goes a long way in achieving different runtime behavior without compromising any other aspect of the logic being executed. Especially you don’t need to recompile anything or change anything.
What’s even more beautiful, when you need polymorphic behavior, you don’t need to use events. Events are great to simplify customization of the application, but using them to achieve loose coupling only introduces problems, more than it solves. Events should be used for zero-footprint changes of standard functionality, whereas dependency injection and inversion of control can be used to achieve loose coupling of dependencies in a robust, bulletproof way.
It’s not time to wake up just yet. CodeunitRef may be one way to solve the problem. I have proposed it because I believe it would be fairly simple to create. Just think of how much work it must have been for Microsoft to support .NET interoperability; I am pretty sure that CodeunitRef would feel like a Monday morning exercise as compared to that work.
A more complicated way perhaps would be to implement another type instead of CodeunitRef: CodeunitInterface.
What that type would do is define the behavior of a codeunit by providing function signatures, without their implementation. Then, a codeunit could implement an interface like this:
Once you specify which interface(s) to implement, you’d need to provide exactly the same functions with exactly the same signatures. And once you do that, you could have a situation such as this:
… which you invoke like this:
What do you think of these suggestions? Would they make your life easier? Please, share your thoughts, I’d like to hear from you.
And now, time to wake up.
Read this post at its original location at http://vjeko.com/i-had-a-dream-codeunit-references, or visit the original blog at http://vjeko.com. 5e33c5f6cb90c441bd1f23d5b9eeca34The post I had a dream: codeunit references appeared first on Vjeko.com.
Расскажите о новых и интересных блогах по Microsoft Dynamics, напишите личное сообщение администратору.
|Navigate Into Success: Gentlemen’s agreement pattern, or handling the “Handled” pattern||Blog bot||NAV: Blogs||0||04.10.2016 12:11|
|Navigate Into Success: Decoupling dependencies in C/AL||Blog bot||NAV: Blogs||0||30.09.2016 16:11|
|Navigate Into Success: Fixing Preview Posting: Part 2||Blog bot||Dynamics CRM: Blogs||0||16.10.2015 08:00|
|Navigate Into Success: I had a dream: decoupled NAV||Blog bot||Dynamics CRM: Blogs||0||19.06.2013 05:44|
|wiki.dynamicsbook: Changes Made in Navision Attain 3.60||Blog bot||Dynamics CRM: Blogs||0||02.09.2008 13:23|
|Опции темы||Поиск в этой теме|