Example Code

Applying Common Object-Oriented (OO) Design Patterns to LabVIEW

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.

Stephen Mercer,

LabVIEW R&D

Design Patterns

  • 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.

Application Examples

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.

LabVOOP

Reference:

Design Patterns. Gamma, Erich, et al. 1995.

Addison Wesley Longman, Inc.

Elijah Kerry
Chief Product Manager, Software Platform
_______________________________________________
Follow my Software Engineering for LabVIEW Blog

Example code from the Example Code Exchange in the NI Community is licensed with the MIT license.

Comments
Daklu
Active Participant Active Participant
Active Participant
on

After reviewing the Rabbit Squad class, I now understand why NI password protects many of their block diagrams--self documenting code at its finest! 

dostini
Member
Member
on

What happened to the Observer Pattern? I'm sure it used to be listed here....

AristosQueue
Proven Zealot
Proven Zealot
on

I removed it. The implementation given was one that I would never recommend anyone use. Someday I'd like to get back to that project, working with the engineer who created it, but it fell off my radar.

Active Participant
Active Participant
on

Why was the link to Observer removed in rev 24?

Elijah_K
Active Participant
Active Participant
on

See previous comment

Elijah Kerry
Chief Product Manager, Software Platform
_______________________________________________
Follow my Software Engineering for LabVIEW Blog
dpnsw
Member
Member
on

Great central point for agregating the various design patterns.

Just noticed this page elsewhere on the forums.

https://decibel.ni.com/content/docs/DOC-21531 for a take on the MVC (Model View Controller) Pattern

Jpina
Member Member
Member
on

AristosQueue wrote:


                       

I removed it. The implementation given was one that I would never recommend anyone use. Someday I'd like to get back to that project, working with the engineer who created it, but it fell off my radar.


                   

Hi Aristos,

when you say that you would not recommend the implementation, are you referring to this one?

https://decibel.ni.com/content/docs/DOC-20182

Could you tell us why would you not recommend that implementation or even the one that Michael suggested? I am working on a project where the observer pattern would really suit, but I'd like to know why it is not recommendable before I get into troubles.

Thank you

SebastienM
Member
Member
on

Hi all,

Maybe it's not the good place to talk about that ...

My concern is about object based framework.

LabVIEW give us since ... I don't remember when it appeared ... the Actor Framework, which is certainly powerfull, but just unreadable.

When I conduct Core3 training, I'm tempted to show what could be object based framework in LabVIEW, then I remember how the actor framework looks, then I decide to make my trainies continue to use LabVIEW, and finally I don't show the actor framework.

When will NI provide a SIMPLE object based framework in the templates ?

Something like the command patern exposed here by Elijal Kerry :

https://ekerry.wordpress.com/2012/01/16/oriented-design-patterns-for-labview-that-can-make-your-life...

or in a the test executive example of the PTP sequencer :

http://www.ptpartners.co.uk/ptp-sequencer/

I personnaly use the "old" QSM (please Daklu, don't bite me), for sure it is not an ideal solution...

I love objects, I used them in my code, but not in for my high level code.

Any links ?

Thanks,

SebastienM

Sébastien
CLA, CTA
AristosQueue
Proven Zealot
Proven Zealot
on

> When will NI provide a SIMPLE object based framework in the templates ?

Your question is confusing to me. I'm going to try to lay out the reason for my confusion... perhaps you can then post a response to clarify your request.

To me, it sounds the same as asking, "When will NI provide a simple polyVI-based framework in the templates?" You don't have a template to demonstrate the language features. You have a template to solve a particular kind of problem. We might have a polyVI in a template if it were approrpriate, but we wouldn't have a template for using a polyVI.

We have examples in the Example Finder for OO. Examples are where we demonstrate features. Check out the board inspection LVOOP example.

Do you just want a template that has a project with nothing but an empty class in it? That was something I thought should be in the templates list but got overruled.

But beyond that, I don't know what you're asking for. Frameworks are meant to solve problems, generally large problems. You don't build a framework for small stuff. The AF tackles a particularly hard problem in LabVIEW (or any programming language). If you are teaching in Core 3 how to build applications that include multiple parallel and intercommunicating processes, you should be teaching the AF or one of the other user-developed-but-freely-available frameworks, as they are all far simpler than trying to code on your own. But last I checked, you're not doing that in Core 3, so you probably shouldn't be teaching AF.

But template projects for "OO" generally doesn't make sense to me. You don't template language features. You template solutions to problems.

As for the command pattern that you mentioned from Eli -- that's *exactly* what the Actor Framework is an instance of. No more, no less. The command pattern as a pattern is not something you can template -- patterns are things that recur in software and you build from scratch each time and they follow a particular form of development but the details are totally different each time. If they could be formalized, they would become a framework or library. The trick is that to make a generic command pattern into to a standalone thing, you need the environment that gets the commanded thing running and the controls for stopping it.

So, instead of asking for an OO-based framework in the templates, tell me a problem that you think has a generic solution worth templating, and then we can discuss using classes to solve that problem. That would be a template that uses OO. But we need a simple problem first.

Do you see the difference? Do you see why even asking the question is confusing to me? Have I missed the point of what you were hoping to see?

Elijah_K
Active Participant
Active Participant
on

I agree with and echo Aristos' comments.

Elijah Kerry
Chief Product Manager, Software Platform
_______________________________________________
Follow my Software Engineering for LabVIEW Blog
BeCeGa
Trusted Enthusiast
Trusted Enthusiast
on

Hi SebastienM

  as Aristos mentiones, it makes not that much sense of just having a object framework in the template without a problem to solve just to show the features. But you can find some libraries like the Configurator Editor Framework (https://decibel.ni.com/content/docs/DOC-37225 ) or the Tag Bus Framework (https://decibel.ni.com/content/docs/DOC-41721 ) which are object based frameworks, for solving specific problems. When you install the VIPMs they will add templates of this implementations to LabVIEW.

Best Regards

Benjamin C
Principal Systems Engineer // CLA // CLED
SebastienM
Member
Member
on

Hi,

First, thank you all for your time, I did'nt expect that LabVIEW stars like you would answer to that

Second : unfortunatly I did a cross post so the conversation was plit in half :

http://forums.ni.com/t5/LabVIEW/SIMPLE-Object-Based-Framework/td-p/3107317

Sébastien
CLA, CTA
SebastienM
Member
Member
on
Not applicable
on

Sorry about my OOO msg... deleting it

george_ford
Member
Member
on

I appreciate all of this effort. I have been personally been working on doing what you have done on my own, and have struggled. I have read this LVOOP overview article  and this discussion concerning the reason for the OOP design choices that were made, and I still find myself in the category of in this quote from that last article of, "<Constructors, being> so fundamental to the language cannot be so completely hidden, and the person feels either ashamed for not being able to find it or angry at us for making it so hard to find!"

 

From the way I am looking at this code, this does not reflect the Strategy Pattern correctly. In the Strategy Pattern, you have a Behavioral interface with specific implementations of said Behavior. So, "HeatBehavior" could be a interface and the implementations would be, "Bake", "Boil", "Broil", "Microwave". The HeatBehavior would provide a "virtual" function, "cook", and each implementation would then implement that function accordingly.

Then, you have a Client interface that is composed of a Behavior object. The Client interface would provide a public function, "doCook", which would call the Behavior object's "cook" function. So, in text language, it would look like (ignoring memory leaks and whatnot):

 

 

class KitchenDevice {
private:
    HeatBehavior* m_heatBehavior;
public:
    void setHeatBehavior(HeatBehavior* hb) {
        m_heatBehavior = hb;
    }
    void doCook() { m_heatBehavior->cook(); }
};

 

 

You then create implementations of "clients" of the "KitchenDevice"

 

 

class Oven : public KitchenDevice {
public:
    Oven() { setHeatBehavior(new Bake{}); }
    // NOTE: the "Bake" class implements HeatBehavior with it's own behavior
};

 

 

 If you are in a rush, you implement the "Microwave", and cook. If you have more time, create the Oven and cook:

 

 

void performCook(KitchenDevice* kd) {
    kd->doCook();
}

int main() {
    Oven*      oven = new Oven{};
    Microwave* mw   = new Microwave{};
    Broiler*   b    = new Broiler{};
    performCook(oven); // performs baking
    performCook(m);    // performs microwaving
    performCook(b);    // performs broiling
    
    return 0;
}

 

 

The potential power of this pattern in LabVIEW becomes clear with, let's say, a power supply.

1. Create behaviors common in interacting with a power supply: SetVoltageBehavior, SetCurrentBehavior, etc

2. Then provide implementations for those: some power supplies communicate via SCSI, some via direct voltage communication. You could even have a 5V supply via a digital switch. All of those will implement the SetVoltageBehavior, SetCurrentBehavior, etc in their own way.

3. Then, create a base client: PowerSupply with implementations of these.

4. You essential create a library of "PowerSupply" implementatins. Whenever you get a new one, you just 1. create a new implementation of the Behaviors and then an implementation of the PowerSupply client.

 

Ok, (writing much more than I intended)...

Having said that, I have been unable to create a LabVIEW class hierarchy that can do this. I won't get into the issues (I've typed enough), but if anyone can provide the above format in LabVIEW code, I'd appreciate it.

Also, if it is already included within the code and I have missed it, apologies.

Thanks

george_ford
Member
Member
on

I discovered where I was going wrong.

  1. I was creating dynamic dispatch vi's in the base client class that set behavior classes. In those setter vi's, naturally, I used the base class of the behaviors. Upon trying to override those setters in the child classes, LabVIEW prevented me because I was trying to write implementations of the base behavior classes into a node that takes a base behavior class in the client's base setter classes (I apologize if hard to follow).
    • SOLUTION: LabVIEW prevents ANY mismatch in types when providing dynamic dispatch vi's. This actually forced me to structure the class more correctly by making me create static dispatch vi's in the client base class to set the behaviors.
    • With static dispatch vi's, the vi nodes can take any child class of the parent class. Again, this is not so with dynamic dispatch vi's (someone please correct if I am wrong)
  2. I then created my own constructor vi's in the child classes. These "constructors" call the static setter vi's from the base class and assign implementations of the base behavior class. Again, since a static vi node that is connected to a base class object can later be used to attach a child object of that the parent class, this is exactly what I do in the constructor vi's.
  3. This means that I never directly use the child classes. Instead, whenever I need an implementation of a particular child class, I place the constructor vi of that class in there.

I'm sure this isn't very easy to follow, but I did want to follow up and mention that I found a solution to my problem.

If anyone knows of a better way than this, particularly that of having to create "constructor" vi's, I'd like to know.

Contributors