Hello, everyone- my name is Andrej Gajduk and I have been developing apps with Mendix for nearly 2 years. Throughout this time, I have frequently stumbled across recurring architectural problems within project components and projects themselves- issues I’m sure every Mendix developer has seen their fair share of. Luckily, there is no need to reinvent the wheel every time such a problem arises. Instead, we can often just use a design pattern, a time-tested solution that can be applied to common design problems. In the following post, I would like to discuss two behavioral patterns and illustrate how they can be used in Mendix.
Behavioral patterns are used to distribute behaviors, i.e. functionalities among objects in an application. This makes an application easier to maintain and extend with new functionalities, as well as more easily comprehensible.
The first pattern we will look at is the strategy pattern; this allows you to define a family of algorithms, encapsulate each one, and make them interchangeable
First, I will review the problem that this pattern attempts to solve with a simple example and then reveal what the pattern looks like in pseudocode and Mendix. However, before continuing I would like to point out that this example and all other code samples from this post can be downloaded as a Mendix project from the following GitHub link:
Imagine that we are building an accounting app and we need to generate a payment report for projects based on the hours worked on it. We might write something like…
This functionality is fairly simple to develop but in an ever-evolving world, software requirements are constantly changing. For example, as soon as we are done building our reporting functionality, the client may tell us that they are introducing an additional way to bill projects. The given rate is $100 per hour for the first 50 hours and then $200 for each hour after that (we will call this overtime pay). We can easily fix this by simply adding an additional enum parameter to our generateReport method/microflow to indicate how the value should be calculated.
Upon further observation, there is an obvious downside to this approach. Every time we want to add a new type of billing, we need to extend the bill_type enum and add another exclusive split. The strategy pattern solves this issue by delegating the computation of value to another object- this uses different object specializations for each alternate way of calculating it. This can be elegantly written in Java using method overriding- if you are not familiar with this concept please refer to this link for more information.
This works particularly well in Java and other OOP languages. Unfortunately, Mendix has no concept of classes or method overriding. At first, I thought that made it impossible to use the Strategy pattern in Mendix. Luckily, my colleagues in Mansystems showed me that there is a way to accomplish something very similar in Mendix, simply utilize object events.
In Mendix, you can define different events for different specializations of an object. When an object is committed, deleted, or rollbacked, the event from the corresponding specialized entity is run. Admittedly, event microflows have limitations- namely, that the only available parameter is the object being committed. Furthermore, it is not possible to return a result from the event microflow however, these can be overcome by storing input and output arguments in the object itself. Armed with this knowledge, we can now attempt to port the logic from Java to Mendix.
Notice that the PayCalculator_Project association is used as an input argument for the commit event and the Value attribute is used to store the output value.
The beauty of this approach is that if someone wants to add a new calculator, they do not need to change anything in the generateReport function. That is the power of the strategy pattern. The pattern can be used in many different ways. Most of the time I find myself using it in Mendix to define callbacks.
The visitor pattern allows an operation to be performed on all elements of an object structure without exposing the internal working of said structure
Through this pattern, it is possible to separate the logic for traversing an object structure from the logic for performing a specific operation on the elements of that structure- this way, multiple operations can be defined without having to copy the traversal logic. The visitor pattern is very handy when working with tree-like structures, such as in the example below:
You can probably see how easy it would be to define another operation, e.g. counting the number of nodes in the tree. All that needs to be done is defining another specialisation of Visitor with the logic that will be applied on each node of the tree.
Perhaps even more importantly, if the internal structure changes then only the microflow Tree_ApplyVisitor needs to change. In fact, this is the only situation where this microflow would need to change, staying true to the single responsibility principle: each function or in the case of Mendix, each microflow should have only one reason to change.
In this blog, I showed how object events in Mendix can be used to emulate method overriding and how the Strategy and Visitor pattern can be effectively applied in a Mendix project. In the future, I would like to take a look at other patterns as well.
I hope you enjoyed reading this blog!