AXForum  
Вернуться   AXForum > Microsoft Dynamics NAV > NAV: Blogs
All
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск Все разделы прочитаны

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 03.02.2019, 00:14   #1  
Blog bot is offline
Blog bot
Участник
 
21,584 / 747 (68) +++++++
Регистрация: 28.10.2006
Navigate Into Success: Codeunit interfaces in AL
Источник: http://vjeko.com/codeunit-interfaces-in-al/
==============

Nearly two and a half years ago I had a dream. It was about codeunit references and codeunit interfaces. And today, nearly two and a half years later I am still here, still having the same dream. We still cannot do codeunit interfaces in AL. But it doesn’t mean we don’t need them. We do. We badly do.

Two things triggered me to write this post about how badly we need codeunit interfaces, or any kind of polymorphism: a NAV TechDays talk, and a github project I saw.



The first one was an NAV 2018 TechDays talk by Torben Wind Meyhoff, Bardur Knudsen, and Jens Møller-Pedersen, all architects at Microsoft, titled “Performance: Business Central reloaded for the Cloud“. In that talk, there is a short discussion on the cost of static event binding, especially in BC scenarios where a lot of static subscribers can negatively influence performance.

However, static subscribers have been at the heart of the so-called handled pattern, a pattern not only proposed by Microsoft, but also actively promoted by them. I say “so called”, because the more I have worked with it, the more I learned that the handled pattern is an anti-pattern. Yes, it does solve a problem. But the solution is not only architecturally weak and full of back-doors for accidental or intentional damage (exactly why I call it the “gentlemen’s agreement pattern”), after the Microsoft architecture team talk at TechDays, it’s also obvious that this pattern also causes potentially serious performance issues.

Think of it for a second. The behavior that the handled pattern attempts to provide is called polymorphism. It has been a holy grail of a sort for a lot of time to achieve it in some way. The need to achieve it was especially obvious when multi-tenancy, and then cloud NAV (today we call it BC) came along.

However, the law of the instrument nicely puts it down: “when all you have is a hammer, everything looks like a nail”. Microsoft must have thought they solved the polymorphic problem with this shiny new golden hammer of theirs: the events. But that was only an attempt at a solution, that in their TechDays talk even Microsoft explained, though from performance angle, was a bad attempt.

Microsoft is now telling us – use manual binding whenever possible. That’s fine, but often, very often, far too often, you cannot use manual binding. Manual binding requires you to know what you are binding up-front, and that’s where all polymorphism dies a very pinful death. This piece of code illustrates it better than anything else:



This is roughly as polymorphic as it gets with manual events. As in any polymorphic scenario, your “provider” is external to the business logic. If you want a third party to provide a specific implementation of the car functionality, there is no way you can do it. Oh, sorry, you can, but only with a statically bound event, which makes the entire manual binding of anything else an exercise in futility. In short, by providing a InvokeManualBinding() event and then InvokeManualUnbindingEvent, you’d have to have a static binding somewhere that then binds something manually.

One thing that I just had to try was variant. This could get us closer to solving at least the generic manual binding problem:



Except that it doesn’t compile. In C/AL at least it compiles but then produces a runtime error. All in all, manual subscribers cannot solve the polymorphism problem. If you need it polymorphic, you need static events.

However, even if the code above was possible, and you were able to bind a manual subscriber codeunit through a variant, it would still result in sub-optimal code and architecture.

First, the code is not straightforward and not easy to read. How do you know exactly who performs the Invoke* operations? How do you know, by looking at this code, that these are operations expected to be performed by one specific subscriber?

And then, how do you guarantee nobody makes an accidental or intentional mess? Like not implementing a specific “method” (that is, not subscribing to a specific event)? Or how do you make sure somebody doesn’t hijack your process by statically subscribing? Yes, you can add the handled pattern on top, but it’s then making it even more difficult to read, adds additional some boilerplate to all your individual polymorhpic method invocations, makes it extra difficult to read ad understand, on top of suffering from all problems of the handled pattern that I described in my referenced blog post.

In the end this is no polymorphism. This is merely a lame attempt at faking it.

But we need polymorphism in our code, we badly need it. If you want to have a robust, extensible architecture, where extensibility is not only a nice-to-have piece of gadgetry but an absolute necessity, an architecture where you don’t control all the components but allow third parties to inject their own components on top, then you need to support real polymorphism.

Precisely because we need it, people have been looking into supporting it ever since AL arrived, and people (other than me) have been asking for it. And people have been inventing wheels, mostly wrong, to support it.

Which brings me to the second thing that pushed me into blogging this.

Shortly after TechDays, also pushed by the comments made by Microsoft architects in their TechDays session, a friend of mine came up with the following proof of concept on github:

https://github.com/vhn/Object-Interface-Pattern

This pattern allows you to do write things like this:



Before I jump into the other thing that pushed me into blogging (again) about a painful need we have for polymorphis, let me give you a simple code example.

I invite you to look closer into the project to understand exactly how it works, but in short it consists of:
  • One interface codeunit that provides the interface methods to be “implemented” by specific “class” codeunits
  • One binder codeunit which handles initial stage of event binding during “construction” stage
  • Any number of individual “class” codeunits which “implement” interface codeunits by subscribing to their events
This is how it works.

You declare a variable of interface codeunit type, and “construct” an instance of it by calling its constructor. To the constructor, you pass the ID of the specific “class” codeunit that “implements” the interface.

The constructor codeunit then invokes the binder through a binder global codeunit variable. This ensures that there is one active binder instance per one active interface instance. Binder codeunit then first binds itself to infrastructure events, then runs the specified codeunit through the codeunit ID.

Then the specific class codeunit kicks in. It has a reference to itself, and passes that reference to its constructor. Its constructor then binds the received reference to events, stores its own reference in a variant variable, and completes the binding by calling its infrastructure event that then retains the bound codeunit reference (through variant) alive throughout its lifecycle.

There on, when you invoke methods on the “interface” instance, you raise events that the “class” instance is manually subscribed to.

The overall result is that once you have all this, you can invoke specific implementations of “interface” functionality simply through the “interface” codeunit variable, just like you would do with interface types in object-oriented languages.

This pattern doesn’t come for free, though:
  • All active manually bound “class” instances must still check whether their individual binding was invoked, on every single event invocation.
  • Anyone eager enough could hijack your binding by statically subscribing to the interface events. You can try to mitigate this by checking the Event Subscription table, but that doesn’t work in extension development (#428, #1715).
  • You could forget to check bindings in your active bound instances, the same problem that the handler pattern suffers from.
  • You could subscribe to the binding infrastructure event, and “steal” the binding ID in case your event fires later in the event sequence.
  • It requires quite some boilerplate. For every interface you need, you must create a binder codeunit which is entirely boilerplate. Interface codeunits require a little boilerplate related to constructing. Every instance (“class”) codeunit requires boilerplate at constructor side, and in each individual “method” event subscriber.
Another problem, that’s not really functional, is that this pattern allows single-implementation “classes”. If you want a single codeunit to implement two different “interfaces”, you can’t have the same instance respond to events belonging to two different interfaces; each “interface” instance would have to be constructed separately, and therefore could not share state.

But still, with all its shortcomings, this is the most beautiful polymorphic pattern I’ve ever seen in AL (or C/AL for that matter, because yes, this will work nicely in C/AL, too). It allows you to provide dynamic modules with minimum performance impact. It’s far simpler than my proposed Module Binder pattern, and has an additional advantage that it allows different instances of the same “type” to exist simultaneously, each with its own separate state.

The sad story, however, is that we in the community even have to come up with patterns such as these. All of this would be so much simpler, if there was a CodeunitInterface type (just suggesting a logical name), that could allow us to write code such as this:



… made possible with something like this:



To not leave it all here, I’ve actually created a new AL issue for this: #4560.

Please share your thoughts on this suggestion, and if you agree that we need something like this in AL, please comment on the issue page on github and vote up this feature suggestion.



Read this post at its original location at http://vjeko.com/codeunit-interfaces-in-al/, or visit the original blog at http://vjeko.com. 5e33c5f6cb90c441bd1f23d5b9eeca34The post Codeunit interfaces in AL appeared first on Vjeko.com.




Источник: http://vjeko.com/codeunit-interfaces-in-al/
__________________
Расскажите о новых и интересных блогах по Microsoft Dynamics, напишите личное сообщение администратору.
 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
saurav-nav: NAV 2018 - Lots of New Objects! Blog bot NAV: Blogs 0 04.12.2017 10:11
Navigate Into Success: Module Binder Pattern proposal Blog bot NAV: Blogs 0 15.10.2016 01:36
Navigate Into Success: From C/AL to executable: how NAV runs your C/AL code Blog bot NAV: Blogs 0 06.10.2016 13:11
Navigate Into Success: Decoupling dependencies in C/AL Blog bot NAV: Blogs 0 30.09.2016 16:11
wiki.dynamicsbook: Changes Made in Navision Attain 3.60 Blog bot Dynamics CRM: Blogs 0 02.09.2008 13:23
Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 21:58.
Powered by vBulletin® v3.8.5. Перевод: zCarot
Контактная информация, Реклама.