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

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 30.09.2016, 16:11   #1  
Blog bot is offline
Blog bot
Участник
 
25,643 / 848 (80) +++++++
Регистрация: 28.10.2006
Navigate Into Success: Decoupling dependencies in C/AL
Источник: http://vjeko.com/decoupling-dependencies-in-cal
==============

Directions US 2016 (yes, 2016, sorry y’all who got the 2017 link in your mailbox) was quite an event. As Directions always is, a lot of people, enthusiastic about this market we strive to thrive in, and about the product we love no matter the limitations we often face when we aim for better, more scalable architectures.

If anything, it reminded me of a long to-do list I have had around for this blog for a while, and I decided to start cleaning it up. The topic of my main session this year was loose coupling of dependencies (I called it polymorphism, because that’s what I’d ultimately like to see possible in C/AL) and I presented two patterns I came up with during my past few years.

Before I present them here on my blog, I wanted to put them in a broader context: loose coupling. So, this is what this post is all about: explaining what loose coupling is, how to achieve it in C/AL, and why you will not want to live without it ever again.


The patterns that I talk in this blog post are about loose coupling. Loose coupling is something you often achieve through polymorphism if you live in an object-oriented world. However, if you have no better tools than C/AL at your hands, we can’t talk about true polymorphism. Still, we can do loose coupling to an extent. So, let’s do it.

Loose coupling is about breaking compile-time ties between dependencies. Dependencies are – obviously – objects that depend on each other. One objects needs to talk to another object, but other than this, they have not much in common. Typically, the other object (the needed object, or dependency) can live a life of its own, without knowledge of the objects that need it..

As a simple example, think of a feature that logs events somewhere when they happen. Like, you release a sales order, and an event is logged in the Windows Event Log. There are many (ugly) ways to get this done. In the pre-events world, and without patterns that help, you’d probably just customize the 414 Release Sales Document codeunit and put your code there. If you were smarter, you’d create an Event Log Management codeunit and call it from Release Sales Document codeunit. But there is a problem – you have just tightly coupled the two codeunits.

Why is this bad? Because if either of components change, the system as a whole may stop working. Also, if you change behavior of the Event Log codeunit, your Release Sales Document codeunit may stop working. In the best case you figure this out at design time (compiler), in the worst case you figure this out from the yelling customer on the phone.

If you want to solve it right, you need a pattern. A good decoupling pattern would have an object that sits between the Release Sales Document codeunit and the Event Log codeunit. In the ideal world, the contract between the Release Sales Document codeunit and this intermediary object would not change. This allows you to change your dependency (Event Log) without affecting the rest of the application.

Enter the Façade Pattern.



Here, you see that the caller object (in our example the Release Sale Document) calls the façade codeunit. The façade has two faces: one towards the caller, and one towards the dependency. The face towards the caller should not change, or should at least be more stable than the face towards the dependency. If you achieve this, if you change the dependency, you do not need to change the remainder of the application, especially not the components that call it.

Consider this: after a while you grow bored of logging events in the event log, and decide to send them to a web service instead, so you can see them on your smartphone. With the façade pattern in place, you can simply add the new codeunit, make the façade aware of it, and your Release Sales Order would now be sending events into your web service without you ever having to change it.

That’s what loose coupling is all about. And while it does call for more legwork, it always pays off.

This is all hunky dory as long as you don’t want to pass more context into your event logging dependency, whatever it be. Like, you suddenly want to send additional parameter to the event logging method. The best way to achieve that in C/AL is by applying the Argument Table pattern. Since it was well described on the Patterns Wiki page, I won’t go into it here. Let me just say that it allows you to arbitrarily change the arguments of the functions without changing its signature. This allows you to change the behavior of the dependency (event log) as much as you want without affecting either the façade or the caller object. Pretty nice, isn’t it?

Now, combine the façade theory with the argument table theory, and you’ve got yourself a recipe for loose coupling that can survive nearly all changes to either consumer or dependency objects, without affecting the compile-time link between them (that’s nearly as loose as it can possibly get) and without causing any runtime issues.

However, the argument table provides much more than just a surface to put more and more arguments on. It gives you possibility to do the only true polymorphic thing C/AL can do: CODEUNIT.RUN. Yeah, right, I know it’s not true polymorphism at play here, but it’s as close as C/AL has ever got to it. It’s polymorphic because it allows one piece of code to execute another piece of code, without having to have any preexisting knowledge about what that other piece of code is or does or anything else about it.

Why is that important? Well, here’s the real deal. To answer that question, I’ll take the step back, and refer to the façade picture above.

That façade codeunit that sits between callers and dependencies – how does it know about the dependencies? Well, if it needs to call into them, then it needs to have at least some knowledge about them. Without CODEUNIT.RUN concept, the only knowledge the façade can possibly have about dependencies is the compile-time knowledge. And while it allows for some flexibility in consuming functionality from dependencies, it in fact for the better part of it simply pushes the problem under the carpet because it still demands that you change the façade whenever you add a new kind of dependency, but also when dependency signatures change.

CODEUNIT.RUN changes all that. The façade does not need to have any compile-time knowledge of dependencies, the codeunits that will be called can simply come from a setup table or anywhere else – it’s only their ID number that’s needed at that point.

So, by combining the argument table, the façade pattern, and CODEUNIT.RUN, you can have a flexible façade codeunit that’s able to serve an arbitrary number of different codeunits that need not even exist at compile time, to callers that need to be aware of anything at all about these codeunits. A holy grail of decoupling, in a way if you want.

Take a look at this beauty:



A piece of code this small can now handle any kind of future change to dependencies without any need to recompile either itself or the caller objects.

However, as nice as it may seem, there are at least two problems with this pattern.

First, as soon as you do CODEUNIT.RUN, you lose the possibility of retaining state inside of dependencies between subsequent calls to their functionality. That’s because there is no variable that keeps an instance of the invoked codeunit alive after the RUN call completes, so as soon as it goes out of scope, whatever state it attempts to retain will be gone for good. Yes, you can make the codeunit single-instance, but that’s not really solving the problem – it only adds a bigger one for you to handle.

You may argue here that you should not retain state inside codeunit globals, but I’ll just say one word: encapsulation.

Encapsulation is good, it exists for a purpose, and I won’t elaborate at this stage further than simply stating this: sometimes it’s necessary to retain state of an object between subsequent calls to that object.

If this last statement were not true, then we’d not be talking about object oriented programming at all. When all objects you have are either static or singletons, or provide only static methods, then all you really do is glorified functional programming.

For this time, I won’t go further into how to solve the state retention problem. I’ll tackle that in a future post.

At this point I want to discuss a different problem that the argument table pattern itself is putting on the table.

That’s the content of the argument table. Since the fields in tables can only have simple types, what happens if – apart from some primitives, you want to pass more than one complex argument to the dependency you are calling (the arguments table itself being the first complex argument).

Well, if that’s what you need, then you can only kiss your CODEUNIT.RUN and polymorphism it brings along a big fat goodbye.

To make it less abstract, consider this. Apart from passing an event id, type and message, you want to pass a customer record, or an item record, or any other record that may be relevant for the call. Or, imagine that you need to pass more than one record at the same time.

Yes, you could pass record IDs, but it comes with problems. First, it requires you to write to the database first (thus starting a write transaction early, or earlier than necessary), and then to re-read it from the database (a waste of time, even with caching in place) and it doesn’t help at all if the record you want to pass is temporary. In either of these situations, the only true option you have is to ditch the CODEUNIT.RUN approach, and go for good old function arguments. And this results in your façade needing to be aware of all the possible codeunits it can handle, requiring you to do a compile-time change to it every time there is a new dependency kid on the block.

So – not good enough.

Apart from that, if the argument you need to pass into the façade is not a record or cannot possibly be represented by a record, the arguments table approach falls apart even sooner. Think of this – you want to pass an instance of a codeunit to a dependency (hard to imagine in a truly loosely coupled world – but hang in tight, I have some crazy ideas about this, too!), or a recordref (a polymorph of a kind) or an instance of a DotNet something or other. In all these cases, arguments table cannot help.

But, a façade can. A variant façade at that.

Now, here we are talking about a pattern that’s both well described in the Patterns Wiki, and also useful beyond its formal description.

This pattern is very similar to the façade pattern, except that it accepts a variant as its most important argument. By using variant, you can pass a record to it without having to first write code to convert it to a recordref, and you can have functions that exhibit polymorphism.

Now, this pattern, while extremely versatile,does not allow you to do CODEUNIT.RUN at all. So, you can end up with a façade that’s uniform to the callers (as any façade worth its mortar should be) but is tightly coupled to the dependencies it serves to interested callers.

And this is, unfortunately, as far as NAV 2015 allows you to go. NAV 2016 allows you to take a step further and jump over obstacle planted by the argument table limitations, but it will both be a topic of another post, and does not do much but place another issue for you to overcome.

As you can see, there are possibilities to achieve loose coupling, but whatever you do, it always comes with a tradeoff. Either your stuff is completely loosely coupled, but only allows stateless calls; or you get stateless calls at the price of tight coupling at the far end of the façade.

In my next post I’ll explain the TempBlob façade, a pattern that I came up with and that has served me well for a couple of years. Until then, I hope the gobbledygook I dumped here today doesn’t scare you off my future posts.

Read this post at its original location at http://vjeko.com/decoupling-dependencies-in-cal, or visit the original blog at http://vjeko.com. 5e33c5f6cb90c441bd1f23d5b9eeca34The post Decoupling dependencies in C/AL appeared first on Vjeko.com.




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

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
Navigate Into Success: Getting out of the DateTime mess, or how to get Utc DateTime in C/AL Blog bot Dynamics CRM: Blogs 0 24.11.2015 21:32
Navigate Into Success: Passing JSON from JavaScript to C/AL–Part 2 Blog bot Dynamics CRM: Blogs 0 29.01.2015 15:13
Navigate Into Success: TRY.. CATCH in C/AL Blog bot Dynamics CRM: Blogs 0 17.10.2014 08:00
Navigate Into Success: A .NET Interoperability Lesson: Mapping indexed properties to C/AL Blog bot Dynamics CRM: Blogs 0 21.08.2014 11:30
Navigate Into Success: Trick: Subscribing to any Control Add-in event through pure C/AL Blog bot Dynamics CRM: Blogs 0 04.08.2014 11:00

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

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

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 22:49.