When talking about computer programming, a design pattern is a standard correct way to organize your code. When trying to achieve some particular result, you look to the standard design patterns first to see if a solution already exists. This sounds a lot like an algorithm. An algorithm is a specific sequence of steps to take to calculate some result from a set of data. Generally algorithms can be written once in any given programming language and then reused over and over again. Design patterns are rewritten over and over again. For example, in house cleaning, consider this algorithm for vacuuming a carpet: “Start in one corner, walk horizontally across the floor, then move to the side and continue making adjacent parallel stripes until the whole floor is vacuumed.” Compare this with the design pattern for cleaning a room: “Start cleaning at the ceiling and generally work your way down so that dirt and dust from above settle on the still unclean areas below.” Design patterns are less specific than algorithms. You use the patterns as a starting point when writing the specific algorithm.
Every language develops its own design patterns. LabVIEW has patterns such as “Consumer/Producer Loops” or “Queued State Machine.” These are not particular VIs. Many VIs are implementations of queued state machines. When a LabVIEW programmer describes a problem, another programmer may answer back, “Oh. You need a queued state machine to solve that problem.” Starting in LabVIEW 7.0, LabVIEW has had VI templates for many LabVIEW design patterns available through the File>>New… dialog. With the release of LabVIEW Object-Oriented Programming, we need to find the new patterns that arise when objects mix with dataflow.
The seminal text on design patterns appeared in 1995: Design Patterns by Gamma, Helm, Johnson and Vlissides. This text is colloquially known as “the Gang of Four book.” This text focused on object-oriented design patterns, specifically those that could be implemented using C++. Many of those patterns are predicated on a by-reference model for objects. As such they apply very well to classes made with the GOOP Toolkit (or any of the several other GOOP implementations for LabVIEW). LabVIEW classes, added in LV8.2, use by-value syntax in order to be dataflow safe. This means that the known patterns of object-oriented programming must be adapted, and new patterns must be identified. This document describes the LabVOOP-specific patterns I have identified thus far.
- Template Method (aka Channeling) Pattern - To provide a guaranteed pre-processing/post-processing around some dynamic central functionality. Allows child classes ability to override some steps of an algorithm without giving them the ability to change out the entire algorithm.
- Factory Pattern - Provide a way to initialize the value on a parent wire with data from many different child classes based on some input value, such as a selector ring value, enum, or string input.
- Hierarchy Composition Pattern - To represent a single object as a tree of smaller instances of that same object type.
- Delegation Pattern - To have two independent classes share common functionality without putting that functionality into a common parent class.
- Visitor Pattern - To write a traversal algorithm of a set of data such that the traversal can be reused for many different operations.
- Aggregation Pattern - To treat an array of objects as a single object and define special behavior for that particular type of array.
- Specification Pattern - To have different functionality for a class depending upon some value of the class without updating case structures throughout your VI hierarchy when you add another value.
- Singleton Pattern - Guarantee that a given class has only a single instance in memory
- Decorator (Wrapper) Pattern - Extend a concrete object’s responsibilities dynamically. Extend the features of an existing class without refactoring tested code.
- Strategy (Policy) Pattern - Change the behavior of a class's method without having to edit the method's implementation.
- State Pattern, Factory Pattern - this is an example of a UI Plugin Framework that illustrates the use of both patterns
- Hardware Abstraction Layer - Illustrates the use of classes and a factory pattern
Those are the design patterns thus far. I hope to update the list as time goes by, both with more patterns and with better examples for the existing patterns. Pay attention to the code you write – when you find yourself following the same routine over and over, try giving a name to the pattern. See if you can clearly identify when it is useful and how best to do it.
Exploration of these patterns helps us design better code. Using the patterns is using the learning of previous developers who have already found good solutions. In studying the patterns, we can learn what good code “feels” like, and we are then more aware when we create an unmaintainable hack in our own programs.
When we follow the standard designs it helps others looking at our code understand what they’re looking at. If a programmer knows the general pattern being used, he or she can ignore the framework and focus on the non-standard pieces. Those points are where the program is doing its real work. It is akin to looking at a painting in a museum – the painting is the interesting part. The picture frame and the wall are just there to hold up the art. If either the frame or wall is too decorative, it pulls the viewer’s focus.
I do not talk about any anti-patterns in this document. Anti-patterns are common attempts that look like good ideas but have some generally subtle problem that will arise late in development. Some anti-patterns in other languages are sufficiently detectable that a compiler could even be taught to recognize them and report an error. Perhaps these exist in LabVIEW. We should be cataloguing these as well.
Let wire and node
gently flow into a world
of class and method.
Design Patterns. Gamma, Erich, et al. 1995.
Addison Wesley Longman, Inc.