From Friday, April 19th (11:00 PM CDT) through Saturday, April 20th (2:00 PM CDT), 2024, ni.com will undergo system upgrades that may result in temporary service interruption.

We appreciate your patience as we improve our online experience.

LabVIEW Development Best Practices Documents

cancel
Showing results for 
Search instead for 
Did you mean: 

Visitor Pattern

(adapted from Gang of Four’s Visitor pattern)

Intent

To write a traversal algorithm of a set of data such that the traversal can be reused for many different operations.

Motivation

Suppose I have an array of integers. I need a function that will calculate the sum of those integers (ignore, for the sake of argument, the fact that there’s a LV built-in function that does this). I drop a For Loop and iterate over every value, adding each element to a running total in an uninitialized shift register. The value in the shift register when the loop finishes is my sum.

Now suppose I want to find the product of those integers. I write the same For Loop and shift register, only instead of the Add primitive, I use Multiply. Everything about these two VIs is exactly the same except for the core action. How can I avoid duplicating so much code?

Dynamic dispatching does not immediately help in this case – I do not have any child types on any of the wires that I can dispatch on. I could make my traversal VI have a VI Reference input. Then, instead of using a primitive, I could use a Call By Reference node. The VI that I pass in would have either the Add or Multiply primitive.

This is helpful, but only works if my two VIs have exactly the same connector pane. What if I need other information, other parameters, to do the job in that Call By Reference node? What if there is more than one running total that I’m keeping track of? This pattern arises from the need to answer these questions.

Implementation

Instead of taking in a VI Reference, the traverse VI takes in an object of class Action. This is a class that you define. Create one child class of Action for each specific action you want to do during the traversal. Then instead of a Call By Reference node, call the Do Action.vi method. This will dynamically dispatch to the correct implementation. The Action object can have all sorts of data inside it that can augment the operation at hand. You can implement new Action children without changing the original traversal framework.

The traversal VI can get very complicated, far beyond the simple For Loop over an array that I mention here. As the traversal walks over a data set, the Action object “visits” each piece of data and updates itself, which explains the name of this pattern. Your visitor can collect a summary of information about the pieces of data (such as the sum and product calculations), or it might search for a particular value, or it might even modify the data as it visits (divide each value in the array by 2). The traversal works just as well in all of these cases.

Editorial Comments

[Stephen Mercer] I am tired of writing LV2-style globals. These globals are great – very dataflow efficient, very easy to understand. But you have to duplicate the VI every time you want to support a different data type. I figured there had to be a better way. I’ve been thinking about this implementation for a while. It may not be optimal, but I think it is an excellent starting point. Imagine… the possibility of never writing another LV2-style global VI ever again, because you could just reuse one and give it the particular action that you want. A whole new flavor of über-geek nirvana!

Elijah Kerry
NI Director, Software Community
Comments
tyk007
Active Participant
Active Participant
on

One of the key purposes of the Visitor pattern is to introduce dual dynamic dispatch into a language that only supports single dispatch (C++, Java, C#, LabVIEW etc.). In this case the traversal algorithm should ideally process each element (IVisitiable) with each operation (Visitor) without knowing the specifics of the actual types involved. This means that processing each element requires two orthognal dispatches - one for the type of element and the next for the type of operation to perform. Think here of a two dimensional table where we choose which method we will actually call to process the element.

It is currently more complex to directly adapt the GoF pattern with LabVIEW at this stage - the second dispatch ideally requires the ability to over-ride methods so that the correct second dispatch implementation can be selected in the visitor hierarchy based on the provided type of the first dispatch (although one could create a named method for each IVisitable type).

Fortunately there are already polymorphic nodes for standard types (e.g. an Add node can take double, int etc.). If the IVisitor operation consists of these polymorphic nodes, and the IVisitable consists of a standard data type these nodes accept, then the pattern can be easily implemented.

nathand
Proven Zealot
Proven Zealot
on

Here's another example of the Visitor Pattern in LabVIEW; this one I think is closer to the traditional use of the pattern. https://lavag.org/topic/16696-ah-yes-another-oo-architecture-question/?p=102428

Contributors