|This article is part of a series. To see more design patterns, please visit Applying Common Object-Oriented (OO) Design Patterns to LabVIEW|
To treat an array of objects as a single object and define special behavior for that particular type of array.
An array nicely collects data together into an indexable list. But there are many primitives that operate on an array: Build Array, Remove From Array, etc. There are times when you want an array to guarantee certain properties, such as an array that:
guarantees that no duplicates are ever inserted
is always sorted
doesn’t allow removing elements
This pattern creates a class that lets you treat an array of another class as a single object.
To begin, identify the invariant that you want the array to maintain. Create a class with a name that reflects that invariant. Is this an array that should always be sorted? Then you might create a class named Ordered Array.lvclass. Perhaps it is an array that has no duplicate elements. Then you might create a class named Unique Elements Array.lvclass. Regardless of the name you choose, you will give the class an array of some data type as its private data. You define methods on the array class that maintain the invariant. So instead of a raw LabVIEW array that allows arbitrary modification using the array primitives, you now have an encapsulated array that only allows the specific actions that you define. For example, if the array is to be always ordered, you would create Insert Element.vi such that it inserts the new element in the correct sorted position in the array. Whatever functionality you supply – or leave out, as in the case of an array that does not allow removing elements – defines what can happen to the array.
The name of this pattern comes from the fact that you are creating a data aggregator – that is, this new class is a data type that represents some aggregation of many instances of another data type. The concept may expand far beyond just a simple array to become any number of collections of data – lists, sets, maps, etc.
The sign that you might want to apply this pattern is when you start referring to an array of the type by some specific name. As an example, you might have a class Athlete. And you have an array of Athletes that you’re passing around and calling “the team”, and you keep checking to make sure that no Athlete is on the same team twice. This is a good sign that you should create a Team class instead of using the array directly.
The major drawback to this pattern is the amount of effort required. It can be a lot of work to re-implement all of the common array operations for your special array type. Fortunately, most of the time when these invariants are required, not all of the array operations are commonly needed.
On the plus side, if all the functions on the class that can insert elements guarantee that the array stays sorted then you can use faster algorithms when searching the array. The Search 1D Array primitive built into LabVIEW does a linear search down the array because LabVIEW has no way of knowing whether the given array is sorted or not. A sorted array can use a binary search (which, if you're not familiar with it, just accept is a much much faster way of searching an array). By limiting the variations of your collection, you may be able to optimize some cases.
You may be tempted to give the new class a wire appearance that looks like an array of your element data type. That’s generally just confusing since the class wire will break if it is wired directly to any of the array primitives.
[Stephen Mercer] Like the Specification pattern, this is an “obvious” pattern. It is included because, as easy as this pattern is, it is even easier just to have a raw array running around on your diagram (you don’t have to write a bunch of accessor VIs to duplicate the functionality already found on certain primitives). There are times when you might choose to do that. But as your code gets more complicated over time, you may find yourself wishing that you had wrapped the array up so that you could guarantee certain characteristics.
There are ideas floating around R&D to allow a class to provide a VI that would define the class’ behavior when it connects to a loop indexing tunnel. That would improve the ability of a class to mimic an array with restrictions on its behavior. It is one of many long-term syntax upgrades we are contemplating.