I’d like to start by wishing everyone a belated happy new year! Time to jump into a new year of LabVIEW programming…
It’s easy to tell code that uses standard design patterns from code that doesn’t – it’s the difference between being able to understand how the application works and having no idea what’s going on without spending a significant amount of time studying the code. This is especially important when working with a team of developers or when developing an application that will need to be maintained over a long-period of time. In either scenario, it’s highly likely that someone besides yourself will have to modify or update the code. This person may not be an expert in exactly what your application does, but if it uses a familiar design pattern, they’ll at least be able to recognize familiar constructs and quickly start contributing.
There are a handful of G-specific design patterns that proficient LabVIEW programmers should already be aware of, including state machines, functional globals, and producer-consumer loops, but LabVIEW programmers should also look to apply concepts from the computer science world to address problems that are universal to all languages. Some of the best-known patterns were first published by a group of authors known as the ‘gang-of-four’ in a book titled ‘Design Patterns: Elements of Reusable Object-Oriented Software’. Since then, the list of object-oriented design patterns has continued to expand to address the growing number of complex software challenges.
One of the first steps when designing software should be to identify where and how standard patterns can be used in your application. This process requires isolating and defining what needs to be achieved in order to meet very specific design requirements, which is a level deeper than the overall architecture of the application. There are numerous examples of patterns and the problems they address, several of which can be found here, but as a starting point, consider the following four patterns and their implementation in LabVIEW.
The factory pattern has been referred to as the most commonly used pattern in modern languages. A factory creates objects without exposing the instantiation logic to the client and refers to the newly created object through a common interface. It’s commonly used in applications that have a plugin component, or some functionality that needs to be loaded dynamically at run-time. This requires the definition of a generic parent object, which encapsulates functionality that can then be reused and even extended by children. [More Info]
Figure 1: This simple snippet of code in LabVIEW enables an application to load children of ‘Generic Plugin.lvclass’ at runtime without having to be aware of the children or their specific functionality ahead of time.
The command pattern is especially relevant to LabVIEW programmers, as it can help address some of the challenges typically associated with sending a command or a message between processes. This typically requires the coupling of the command itself with any data required by that command to execute, which is often achieved by using a cluster containing the command as an enumerated constant and a variant for an arbitrary data-type. The command pattern instead instantiates a new instance of a child class to represents the specific command, which defines the information to be associated with that command within the private data. [more info]
Figure 2: The command pattern uses child classes to represent commands or messages that need to be sent to a consumer loop. Data associated with the command is stored within the object, and the execute method is overridden by each specific class to implement the functionality required to handle the specific message.
As the name implies, the observer pattern can be constructed in LabVIEW such that an observer is aware of changes made to the parent observerable class. In this particular example, the observable class has a method that modifies the data of a class that is referred to by a data value reference. Throughout the lifetime of the observable class, multiple observers may subscribe and unsubscribe. [more info]
Figure 3: In this example, two string indicators are subscribed to an observable class, ‘Text Message.lvclass.’ The observable class stores an array of data value references to observers, which are then retrieved and modified when the method to update observers is run.
Applications are often designed with the knowledge that future functionality will need to be added at a later date, but this is best done without the risk associated with modifying pre-existing code that is tested. The decorator pattern can help in such a scenario, by making it possible to add responsibilities and information dynamically to an object. [more info]
Figure 4: The original object, which is a Dark Roast coffee, is decorated with additional properties by children of a separate hierarchy for condiments. The set of condiments can dynamically be expanded without modifying peers. Invoking the methods to get the cost and the description recursively iterates through all the decorations to aggregate the total values.
These four patterns are just a small number of simple examples illustrating how object-oriented design patterns can be used to solve common problems. To download examples and see many more examples of how object-oriented design patterns can be applied to LabVIEW, visit the community site Applying Common Object-Oriented (OO) Design Patterns in LabVIEW