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.
Your data has some field – perhaps an enum, perhaps a string, perhaps something more complex – and you have a case structure that cases out on that field to decide which of several actions to perform. The data in this field is “constant,” meaning that once you have initialized the class this field never changes. When you find yourself creating a lot of these case structures, or, worse, updating a lot of these structures to add a new case, you should remember this pattern.
Examples of this pattern occur in just about every shipping example. Any time you have a child class that overrides a dynamic dispatch VI defined by the parent class, you’re seeing an application of this pattern.
If some data element is constant from the time the cluster (or existing class) is initialized onward, then you should strongly consider creating a set of child classes. The data in that field is unnecessary – stop hauling it around the diagram. A class knows its data type. For any case structure that used to select based on the type, create a new dynamic VI on the parent class and put the code that used to be in each frame of the case structure into the children’s override VIs. Instead of one monolith cluster with an enum type, you now have a parent class and many more specific classes – thus the name of this pattern.
The parent class is frequently one that you never expect to actually see traveling on a wire in your application. It exists merely to define the API that all of your child classes will implement. Such parents are frequently referred to as “abstract classes” because you never expect them to be physically instantiated on the wire.
This refactoring typically results in many more VIs in your project. This can have a load time impact on your application as a whole. But your run time benefits may make this worth it since there is less data being copied around for each object than for each pre-refactoring cluster.
This refactoring assumes that you’re going to change all existing case structures that select on type into calls to dynamic dispatch VIs. However, you may not have time to change all the structures, or the structures may be on VIs owned by other developers. If you encounter a situation in which you cannot create a specific dynamic dispatch VI for each case structure, you can still create a single dynamic dispatch VI called Get Type.vi and have each child override it to return the original data. Just call that method and pass the results into the existing case structures. You won’t be taking full advantage of dynamic dispatching, so you’ll still have to update those case structures when adding new child classes, but at least you won’t be copying the data of that field every time you copy your object.
If you cannot add Get Type.vi to your hierarchy (perhaps because the class is password protected), there are other ways to do type testing. The most obvious is the To More Specific function, which returns an error if the given data is not of the target class. So you can test “Is it this class? No. Is it this class? No. Is it this class? Yes.” Other ways of doing type-testing exist; you may find them documented elsewhere.
This pattern is frequently used in conjunction with the Factory pattern. When refactoring existing code, you may find the Delegation pattern helpful to create a good class hierarchy.
[Stephen Mercer] This is an obvious pattern to anyone who has grasped the basics of object-oriented design. This is an easy pattern to understand, and it is easy to think of the implementation on your own. Not all the patterns are complex systems. Sometimes pointing out the obvious is useful. When planning a new application, you may look at a list of patterns to consider what the best design would be. Having these so-called “obvious” patterns in the list helps remind you that you ought to use them. It may seem silly to mention Specification as a pattern because this pattern is nothing more than the central use case for class inheritance and dynamic dispatching. I highlight it as a pattern because even experienced designers sometimes overlook the obvious. They may miss the fact that a piece of data in their cluster never changes. If data never changes, that data is effectively part of the data type, and rather than carry that information around as data, which has to be copied at wire forks and other junctures, it can be carried as part of the data type, with its behavior encoded in various VIs which do not require copying all the time. Clusters that carry around an enum of how to interpret its cluster elements or a string representing the source of the cluster data cry out for application of this pattern. This pattern simplifies complex VIs and large clusters into manageable chunks by giving an easy way to break up the functionality of the case structure across multiple VIs. This pattern is the first one that a new user of object-orientation should learn and is the first one to consider when refactoring existing applications.