Design Pattern Case Study: A Simple Counter

From LabVIEW Wiki
Jump to: navigation, search

by Quentin"Q" Alldredge, Q Software Innovations, LLC

What design pattern should I choose? The design pattern you choose is highly dependent on the project requirements. Here is a case study implementing a simple counter in many of the design patterns described in the Design Patterns Overview page.

Design Pattern Scale

Source code

All of the source code is available at:

QSI Shared Code on GitLab for LabVIEW 2018 For LabVIEW 2015

Requirements

The Program Shall:

  • Provide a way to allow the user to choose between Increment and Decrement
  • Provide a way to allow the user to Stop the program
  • Provide an I32 Numeric Input called Amount
  • Provide an I32 Numeric Output called Count
  • When Increment is clicked, add Amount to the Count
  • When Decrement is clicked, subtract Amount from the Count
  • The UI must look like this (title can be different):
Simple Counter User Interface

State Machine

The State Machine polls for user input which changes the state to Increment or Decrement. If both are FALSE the state is transitioned to Idle. A Wait is required but was made small to prevent any lag which the user could see. Undefined states cause an error and the loop to stop.

Simple Counter implemented using the State Machine Design Pattern
State Machine Pros and Cons
Pros Cons
Simple to map flow in a flowchart and create Either used with no UI or has to poll for user interactions which requires a Wait function to keep from taxing the processor
Simple to extend by adding cases and modifying state transitions Not meant for multiple processes that have to run at different rates
Can't handle the window close button (disabled in this example) so there has to be something else to end program
UI becomes unresponsive if code takes a long time to execute
If using Strings to define state, the default case must be handled. It would be better to implement with an enum.

Event Handler

The Event Handler operates only when there is user interaction. In this case it is when the user clicks Increment, Decrement, Stop or the Windows Close button, "X". The code for each operates inside of the Event Structure.

Simple Counter implemented using the Event Handler Design Pattern
Event Handler Pros and Cons
Pros Cons
Does not have to poll for user interactions which reduces processor resources Completely dependent on user interactions
Can handle the windows close button event Not meant for multiple processes that have to run at different rates
UI becomes unresponsive if code takes a long time to execute

Master/Slave

The Master/Slave separates the Event Handling into the Master Loop and the Data Processing into the Slave Loop. All the Master Loop does is issue commands while the Slave Loop executes those commands. If the Slave receives a command it doesn't know how to do it does nothing. The communication between them is a Notifier which is lossy, it can only send one command at a time. If a command is sent too quickly, it could be lost.

Simple Counter implemented using the Master/Slave Design Pattern
Master/Slave Pros and Cons
Pros Cons
Does not have to poll for user interactions which reduces processor resources Commands issued too quickly can be lost
Can handle the windows close button event If using Strings to define commands, the default case must be handled. It would be better to implement with an enum.
Separates UI from code execution to keep it responsive Commands do not have data or what to execute. It is up to the Slave Loop about what to do with each command.
Can have multiple Slave Loops

Producer/Consumer

The Producer/Consumer is closely related to the Master/Slave but instead of issuing commands the Producer/Consumer produces data in the Producer Loop which is the used, or consumed, in the Consumer Loop. The communication between them is a Queue which is will buffer the data so that none of it is lost.

Simple Counter implemented using the Producer/Consumer Design Pattern
Producer/Consumer Pros and Cons
Pros Cons
Does not have to poll for user interactions which reduces processor resources Can't have multiple Consumer Loops
Can handle the windows close button event and other events The structure of data is set for the Consumer Loop.
Separates UI from code execution to keep it responsive It is up to the Consumer Loop about what to do with the data.
Buffers data so that none is lost

Action Engine (AE)

The Action Engine (AE) implementation is similar to the State Machine design pattern. It uses an uninitialized shift register as a memory space for its data (the current count in this case study) and contains the actions to carryout ("Initialize", "Increment", "Decrement", "Stop", and Default). It does not implement the UI requirements and has to be used in an owning program which implements another design pattern. (However, if the above example of a QMH left the Count State shift-register uninitialized, the bottom Message Handling Loop could act as an Action Engine of sorts.)

Simple Counter implemented using the Action Engine Design Pattern (While Loop)
Simple Counter implemented using the Action Engine Design Pattern (For Loop)
Simple Counter implemented using the Action Engine Design Pattern (Feedback Node)
Action Engine Pros and Cons
Pros Cons
Extensible Can add more actions. If using Strings to define action, the default case must be handled. It would be better to implement with an type definition enum.
Encasulates code to be used in multiple places but operates on the same data Not usually used as a stand alone design pattern. Usually doesn't contain UI

Queued Message Handler (QMH)

The Queued Message Handler (QMH) design pattern is a combination of Producer/Consumer, Event Handler and State Machine architectures together. It combines the command and data into a Message Queue that serves as the communication between the Event Loop and the Message Handling Loop. The QMH also defines communication back the other direction, from the Message Handling Loop to the Event Loop, through the use of User Events. Unhandled messages cause an error that stops the program.

Simple Counter implemented using the Queued Message Handler Design Pattern
QMH Pros and Cons
Pros Cons
Does not have to poll for user interactions which reduces processor resources Can't have multiple Message Handler Loops
Can handle the windows close button event and other events If using Strings to define command, the default case must be handled. It would be better to implement with an enum.
Separates UI from code execution to keep it responsive
Bi-directional communication between loops
Each command can have a different structure of data associated with it.

Object-Oriented QMH

The Object-Oriented QMH uses Objects as the messages. These objects are defined by the Message class and include a must override method called Action. The Message Queue, Update Count User Event and Stop User Event are created in the State Initialize Method. Messages are sent using the Message Queue from the Event Loop to the Message Handling Loop where they are dequeued. The dequeued Message then calls its Action Method by dynamic dispatch which performs an Action on the State.

Simple Counter implemented using the Object-Oriented QMH Design Pattern
OO QMH Pros and Cons
Pros Cons
Extensible. It is easy to extend functionality by adding a new child of the Message Class. Lots of files to manage. Every message is at least three files (Class, Action Method and Send Method).
Messages contain the command (Class), the data (Class Private Data), and the action (Action Method). Does not handle communication between modules without adding another communication method.
Does not use strings to define commands.

Delacor QMH

The Delacor Queued Message Handler (DQMH) design pattern is an extension of the QMH. Each module uses the same structure for messages as the QMH (cluster of command string, called Message, and data variant, called Message Data) but this time the queue is wrapped in an class called Message Queue. The DQMH also adds external communication between asynchronously running modules by using User Events. Adding new modules and new events are accomplished through built in scripting tools. A stand alone tester is also created along side each module.

When implementing this example the UI could have been part of the module. However, it seemed more correct to use a variation of the tester that was created as the UI. In this way it shows the user event communication. The design pattern of the tester and subsequently the UI VI is the Event Handler design pattern.

This requires the DQMH Tools from the LabVIEW Tools Network.

Simple Counter implemented using the Delacor QMH Design Pattern (Business Logic Code)
Simple Counter implemented using the Delacor QMH Design Pattern (UI Logic Code, Event Handler)
DQMH Pros and Cons
Pros Cons
Extensible. Extend functionality using built in scripting tools executed from the Tools Menu. Lots of auto-generated code with notes not to touch. Accidentally editing some things could break the scripting tools.
Message creation and handling is managed via the tools. Uses strings to define Messages.
Provides a lot of in code documentation to help know how and where to modify. It can be confusing when starting with this framework to know where to add your custom code. Be sure to follow documentation bookmarks.
Adds external messaging between modules above that of the regular QMH. Which allows for multiple asynchronous processes.
Adds easy testing because a Tester API is created along side all modules

Actor Framework

The Actor Framework design pattern is very similar to the Object-Oriented QMH. It also uses Objects as the Messages, inheriting from a special Message Class. The Messages also define the command (the message object itself), the data (the private data of the class), the action to carry out (the DO Method), and the way to send the message (the Send Method). The Message Handling Loop is completely abstracted away as the Call to Parent Actor Core Method. Main UI and the State is defined in the override of the Actor Core, shown below.

Simple Counter implemented using the Actor Framework Design Pattern
Actor Framework Pros and Cons
Pros Cons
Extensible. It is easy to extend functionaily by adding a new child of the Message Class. Lots of files to manage. Every message is at least three files (Class, Do Method and Send Method).
Messages contain the command (Class), the data (Class Private Data), and the action (Do Method). Difficult to debug. Using MGIs Monitored Actor helps.
Does not use strings to define commands. If editing the method a Message is based on, it doesn't update the message. This is fixed with MGIs Improved Actor Framework Message Maker.
Similar to the OO QMH but adds external messaging between modules. Which allows for multiple asynchronous processes. It can be confusing when starting with this framework to know where to add your custom code.
Only one communication route. External modules can send the same Messages to a module that it can send to itself.

QControls

This is definitely not the best use case for a QControl. QControls are Object-Oriented replacements for XControls. However, for science lets try it.

Like the FGV a QControl is not a stand alone operator; therefore, it needs to be in another VI to function. The Increment, Decrement, Amount changing, and the actual Count is handled by the QControl. The Stop button is handled by the owning UI. Count is updated via a User Event started by the QControl. Under the hood its just two Event Handlers; one for the QControl and one for the owning VI. The owning VI could be any design pattern. The QControl should not need anything more than an Event Handler but it could probably be a QMH if necessary.

QControl, Event Handler
Owning VI, another Event Handler
QControls Pros and Cons
Pros Cons
Replaces XControls It can be confusing, when starting, to know where to add your custom code. See Help->QSI->QControl Toolkit Help...
Did I mention it replaces XControls? Again, not the best use case for QControls.