You Already Know Enough to Get Started
If you do not currently leverage object-oriented programming techniques in your LabVIEW code (believe it or not) you likely know more than enough to get started. Object-oriented programming is not magic—it is a programming style built for modularity, and maintainability. As a regular G developer, you probably encounter clusters, libraries, type definitions, property nodes, and polymorphic VIs on a daily basis.
Mix these concepts together, give them a little stir and… BOOM!
What is a class?
A class is simply an object and a set of methods that act upon it. Translating into LabVIEW terms:
- Object – A LabVIEW object is just a cluster with special abilities (a private data cluster)
- Method – A LabVIEW method is nothing more than a VI belonging to a class library
- Class – A LabVIEW class is simply a unique type of library that always contains one private data cluster (sometimes we refer to classes as abstractions or types)
A class takes care of its own business and, ideally, this business is limited in scope. When a class does too many things, the class likely requires frequent modification, probably loses reusability, and generally fails to provide the benefits of object-oriented programming.
In this post, I dissect one of the three foundational principles of object-oriented programming in the context of LabVIEW: inheritance. Look for posts on the other two principles (encapsulation and polymorphism) sometime soon.
Pillar One: Inheritance – Classes can have parent/child relationships.
If you ever use property or invoke nodes, the concept of class inheritance might sound familiar. We often use a property node to programmatically change properties through VI Server. While object-oriented programming in LabVIEW is not an extension of VI Server (LVOOP and VI Server use separate class hierarchies), using property and invoke nodes means that you already leverage inheritance in your LabVIEW code.
You will enjoy object-oriented programming more with a basic understanding of the terms.
- Hierarchy – A structured set of inheritance-based relationships among classes
- Parent – A more general class, having at least one child
- Child – A more specific class, descending from one parent class
- Ancestor – A more general class in a hierarchy; parent, grandparent, etc.
- Descendant – A more specific class in a hierarchy; child, grandchild, etc.
- Leaf-Level - Having no children; the most specific class in a hierarchy
You Already Use Inheritance
Consider this right-click menu accessible from a property node linked to a Boolean control:
We can access general properties higher in the list and specific properties of a Boolean control lower in the list. The list implies a hierarchical inheritance relationship between object types. A Boolean is a type of Control and a control is a type of GObject. We intuitively know that changing a property called Boolean Text would not make sense if our control were, instead, a string or a knob. Voila—an example of inheritance!
The code below demonstrates calling a single property node on three different reference types. The code shows no broken wires and functions properly because the Visible property applies to all of the wired types:
Create Your Own Class Hierarchy
Create some classes of your own. (Right click>>New>>Class)
Set class inheritance relationships with a few additional clicks, noting that your classes will always inherit from LabVIEW Object. (Right click on class>>Properties>>Inheritance>>Change Inheritance…)
View your class hierarchy at any time by selecting View>>LabVIEW Class Hierarchy from any VI or project. While this view does not communicate everything about an application’s design, it might help you get your bearings while navigating an object-oriented project.
Your LabVIEW classes can inherit a few fundamental things from an ancestor:
- Data – If the ancestor has data, the descendant also has it
- Functionality – The descendant can call functionality written into its ancestors
Note that inheritance can also transfer options and obligations to descendant classes. Understanding these two powerful object-oriented relationships also relies heavily on another principle—polymorphism—to be covered in a separate post.
To visualize data inheritance, consider this snippet of a real class hierarchy showing an abstraction called Layout and its three children: TabbedLayout, ConfigurationLayout, and QuadLayout. (The Layout class allows an application to fundamentally change the structure of its user interface by simply loading a specific child type at run-time.)
The parent class (Layout) has only what all layouts need to do their job (an array of references to viewable classes). The child classes have additional specific data required to arrange and show viewable classes in their unique, fully concrete, way.
If we reconsider this hierarchy as class private data clusters (objects), it looks like this:
In actuality, each child object contains its data and the data of its ancestors. While the children do not have direct access to their ancestors’ data, they do indeed contain the full set (more on accessing the parent data in my next post on encapsulation).
...But rest assured, children of Layout really contain the parent data and the unique child data:
In addition to data, child classes can inherit functionality. If, for example, we write some useful methods in an ancestor class, we neither need nor want to rewrite that code in the child classes. Thus, we can quickly recognize the power of inheritance as a mechanism for software reuse—methods can be implemented once in an ancestor and reused by all descendants.
Expanding our view of the class hierarchy above, we can see that Layout indeed inherits from another class. This higher ancestor, named Viewable, includes useful methods for inserting into and removing from a subpanel.
The three specific Layout chidren (examples of leaf-level classes) do not re-implement the Insert and Remove methods. They simply use their Viewable ancestor’s implementation:
Look, Ma, no broken wires! Similar to the property node example from above, we can wire three different object types into a single parent method. Courtesy of inheritance, LabVIEW knows this Insert method applies to all of the descendants.
Object-orientated thinking is a powerful way to solve tough software problems, which explains why OOP is a standard feature of most modern programming languages. This post is a primer on object-oriented inheritance in LabVIEW. Look for posts covering encapsulation and polymorphism in the near future.
You might also wonder, “How do I design an application using classes?” While this is a valuable question—worthy of many more blog posts—I urge you to simply get started and to enjoy the climb!