LabVIEW Craft
  • Blog
  • About
  • Contact
  • Tools
  • Resources

Fun with the Actor Framework: Low Coupled Messaging

8/12/2015

13 Comments

 
Jon McBee
As writers of software we strive to craft code with high cohesion and loose coupling.  This can be tough to do with the Actor Framework as the default method of sending messages between actors effectively couples them together.  Depending on your application this coupling may not matter, but I will show you one way to manage it if it does.
Picture
The above image illustrates a small section of the flow of control for an application used to calculate properties of a black hole.  There is a high level module used to calculate the tidal force that objects experience while near a black hole's event horizon.  This tidal force calculation requires the black hole's mass as an input, which is calculated by a lower level module.  The high level tidal force module needs the black hole's mass in order to perform its calculation, and it depends on the low level mass module to supply it.  
Quick Note: Based on some feedback I received on this post I posted a simple UML primer
Picture
The image above shows the source code dependencies between the different classes that make up the black hole application.  Lets take a look at the LabVIEW code that implements these functions.
The Calculate Tidal Forces Actor calls the Send method of the Calculate Mass Message, which invokes its Do method.  
Picture
Picture
The Do method of the Calculate Mass Message calls the Calculate Mass method of the Calculate Mass Actor, which performs the mass calculation.  
Picture
The Calculate Mass method of the Calculate Mass Actor uses the Send method of the Calculate Force Message to pass the calculated mass back to the Calculate Tidal Forces Actor.  
Picture
Calling the Send method of the Calculate Force Message invokes its Do method, which calls the Calculate Force method of the Calculate Tidal Forces Actor.
Take a moment and compare the image showing the flow of control of the application to the image showing the source code dependencies.  The run time dependencies between modules as dictated by the flow of control are the same as the source code dependencies.  If we ever expect to use the the Calculate Mass Actor without also using the Calculate Tidal Forces Actor (which is a very reasonable expectation to have, as the mass is an input to many other black hole property calculations) then we will need to break the source code dependency between the two modules, inverting it against the flow of control.
Picture
This inversion of dependencies is known as the Dependency Inversion Principle (DIP), and is one of Bob Martin's five S.O.L.I.D. principles of object-oriented design, which you can read about in the book shown above.  The DIP states that:
A. High-level modules should not depend on low-level modules. Both should depend on abstractions.
B. Abstractions should not depend on details. Details should depend on abstractions.
We can invert the Calculate Tidal Force Actors dependency on the Calculate Mass Actor by creating an Abstract Calculate Mass Actor that defines an interface for mass calculation, and having Calculate Mass Actor inherit from it.
Picture
Notice the arrow illustrating that the Calculate Mass Actor implements the Abstract Calculate Mass Actor opposes the flow of control.  This is the "Inversion" in the Dependency Inversion Principle.
Picture
By creating the Abstract Calculate Mass Actor and having the Calculate Mass Actor inherit from it we have satisfied the two parts of the definition of dependency inversion.  
A. The high level module, Calculate Tidal Forces Actor, no longer depends on the low level module, Calculate Mass Actor, it now depends on an abstraction.
B. The Abstract Calculate Mass Actor defines an interface for calculating the mass of black hole but does not define the details of that calculation; the details are defined by the Calculate Mass Actor, which implements the abstraction.
The only difference from our codes' point of view is that now the Do method of the Calculate Mass Message depends on the dynamically dispatched Calculate Mass method of the Abstract Calculate Mass Actor instead of the Calculate Mass method of the Calculate Mass Actor.
Picture
We are not entirely out of the woods yet though.  While the Calculate Tidal Forces Actor no longer depends on the Calculate Mass Actor, the Calculate Mass Actor does still depend on the Calculate Tidal Forces Actor.  This dependency exists because the Calculate Mass method of the Calculate Mass Actor still calls the Send method of the Calculate Force Message, which invokes the Do method of the Calculate Force Message, which depends on the Calculate Tidal Forces Actor (This is easier to convey graphically than textually...)
Picture
The fact that the Calculate Mass Actor depends on the Calculate Tidal Forces Actor is a problem because we want to be able to use our Calculate Mass Actor with other modules without having each new module cause the Calculate Mass Actor's dependency tree to grow.*
*There is an assumption here that we actually do want to reuse the Calculate Mass Actor (or at least use it in a way that would cause us to benefit from cutting the dependency, like wrapping it up inside of a Packed Project Library), and that cutting this dependency will add value to our application(s).  However, this may not always be the case, inverting dependencies for the sake of following DIP is not always necessary.
Needing to decouple the Calculate Mass Actor from its caller completely will be more difficult than decoupling the Calculate Tidal Forces Actor from the low level module that implements the Calculate Mass function.  We can't simply create an abstraction for the Calculate Tidal Forces Actor because that would imply that the Calculate Mass Actor had a policy of needing to pass the result of the mass calculation back to a Calculate Tidal Forces interface.  We need to be more generic than that, the Calculate Mass Actor's policy should be that it needs to pass the result of the mass calculation back to whoever requested the calculation in the first place.  To satisfy this policy we will need to create an abstraction around the Actor Framework Message class.
Picture
We have created a message classed named Low Coupled Message and made it a child of the Actor Framework Message class.  We can now create abstract children of the Low Coupled Message that define data types for messages, and have accessor methods that allow getting and setting data in the object.  These abstract children define the data but do not override the Do method and so do not define how the data is used.
Picture
Because the Low Coupled Message does not override the Do method, which the Actor Framework Message class has marked as Must Override, we need to set the flag for "Transfer all Must Override requirements to descendant classes" on the Inheritance page of its class properties.  The same needs to be done for the abstract data classes, as they also do not override the Do method.
Picture
Notice that it is possible to put a self addressed message into the class data of an abstract data class.  This is illustrated in the UML above by showing that Abstract XYZ MSG has an ABC Message in its class data.  This approach allows the ABC Class (not pictured) to send the Abstract XYZ MSG to the XYZ class (not pictured) while specifying that the XYZ class send data back via the ABC Message.  This is equivalent to me mailing you a letter and putting a self addressed envelope inside the envelope I send you along side the letter, so that all you have to do is put your own letter in the envelope I provided and drop it in the mail.  
We can apply the Low Coupled Message concept to our black hole property application by changing the Calculate Force Message and the Calculate Mass Message classes inheritance.  
Picture
Notice how the Calculate Mass Actor no longer depends on the Calculate Force Message class, it now depends on both the Low Coupled Message and "Abstract Return Mass Message" classes.  We have now inverted all source code dependencies against the flow of control.
We can take a tour of the software to see how this UML translates to code.
Picture
The Calculate Tidal Forces actor sends an Abstract Calculate Mass Message to the Calculate Mass actor that was injected at run time.  Included in the payload of the Abstract Calculate Mass Message is a self addressed Calculate Tidal Force Message that the Calculate Mass actor can use to return the mass once it has been calculated.
Picture
The Send method of the Low Coupled Message class is just a wrapper around the Actor Framework Enqueue method.
Picture
The Do method of the Calculate Mass Message class uses the accessor methods from the Abstract Calculate Mass Message class to extract the data (including the return message) and pass it into the Compute Mass method of the Calculate Mass class.
Picture
The Calculate Mass method performs its calculation and then uses the Write Mass accessor from the Abstract Return Mass Message to pack the data into the reply message and send it using the Send method of the Low Coupling Message class.
Picture
The Do method of the Calculate Force Message use the Read Mass accessor from the Abstract Calculate Force Message to extract the mass and then pass it into the Calculate Tidal Force method of the Calculate Tidal Forces actor.
By making these changes to how our actors communicate with each other we are able to decouple our actors from each other.  In the example above we used the Abstract Calculate Mass Message class and the Abstract Return Mass Message class to create a generic way for high level modules (like the Calculate Tidal Force actor) to communicate with our low level Calculate Mass actor.  This generic communication was achieved by inverting the source code dependencies between the modules, while leaving the run time dependencies the same.

Jon McBee is a Principal Software Engineer at Cambridge NanoTech and is a Certified LabVIEW Architect, Certified LabVIEW Embedded Developer, Certified TestStand Developer, an NI Certified Professional Instructor, and a LabVIEW Champion
example_code.zip
File Size: 1359 kb
File Type: zip
Download File

13 Comments
Matt Gould
8/17/2015 12:17:40 pm

This is really important information, but the use of UML really obfuscates this for me. Most LabVIEW programmers are not software engineers by training, so the use of UML makes this really hard to understand.

Is there any chance the source code for this application would be posted?

Reply
Jon McBee
8/17/2015 04:06:54 pm

Hi Matt,

I just posted the source for you. It is in LV 2014, let me know if that works.

Thanks
Jon

Reply
Jon McBee
8/17/2015 12:29:22 pm

Hi Matt, I can definitely post the source for you. You raise an interesting point with the UML, one that may be worthy of its own blog post.

Reply
Matt Gould
8/20/2015 04:59:35 am

Thanks Jon, do you have a good explanation of what the different arrows mean in UML? I understand a white arrow shows inheritance, but the black arrow and diamond arrows are less clear to me.

Reply
Jon McBee
8/20/2015 06:10:38 am

Hey Matt,

I put together a short post on UML that can be used as a primer to my ramblings: http://www.labviewcraftsmen.com/blog/simple-uml

Thanks,
Jon

Reply
Matt Gould
8/21/2015 07:43:31 am

Your the man Jon!

Reply
Matt Gould
8/24/2015 01:22:42 pm

So, if you were to package up the Mass Actor and it's messages,to give to another developer on your team to use in a totally separate project, what would you actually package?

Likewise for the calculate tidal force actor and it's associated messages. Would you include the abstract Calculate Mass Actor in both sections of code or just one?

I guess I am assuming that the point of dependency inversion is that inheritance is not considered a dependency. Is that correct? In other words, if Class B inherits from Class A, then Class B is not dependent on Class A. It seems like in Labview that if one class inherits from another, there is an implicit dependency because when you open the child, Labview is going to look for the parent.

Reply
Jon McBee
8/29/2015 04:22:45 am

Hey Matt,

Sorry it has taken so long to get back to you, it's been a crazy week. I don't have access to the code right now so I will try to answer from memory.

Packaging is a problem domain all of its own, and there are even a set of principles associated with how best to do it (courtesy of Robert Martin). If my goal was to make the code as atomic as possible I would create a package for the Abstract parent and its messages, and packages for each of its children where each child inherited from the packed parent. I could use the packed parent to define an interface for the children and give the parent interface package to other developers to build mass calculating plugins.

I would say that there is definitely a source code dependency between parent and. Hold classes. Where dependency inversion adds power is when you look at runtime dependencies. There as an important distinction between source code dependencies. Other modules can depend on the parent interface package as a source dependency but at runtime a child can be injected and used as a runtime dependency. It is this inversion that we speak of when we talk about dependency inversion.

Clear as mud?

Reply
Matt Goudl
9/3/2015 07:41:55 am

Thanks for taking the time to answer Jon! I think your last paragraph actually cleared up my understand a little more.

A couple questions:

Would you recommend Robert Martin's book for non-software engineers? Is it a very dense read?

I've seen other blog/forum posts from other sources that say low or zero coupling solutions should be used very sparingly. Do you agree with that? How do you determine when you are going to insert and abstraction layer?

Reply
Jon McBee
9/4/2015 05:30:56 pm

Hey Matt, I'm glad that helped. I would definitely recommend The Agile Software Development book, I strongly believe it will help anyone write better code (SE or not).

The second question is a bit harder to answer. In fact it's a lot harder to answer, I know because I have started and stopped a few times now. I think it may deserve its own blog post, which I will write as soon as I get my hands on a computer, doing this through the app on my phone is tedious. If you end up getting Bob Martins book the core of the answer to your questions on low coupled messaging and abstraction layers will be found in the DIP and OCP chapters respectively.

Sorry for coping out on the hard questions, but I will cover them in a new post asap.

Reply
Paul Sammut
5/12/2016 11:18:52 am

I thoroughly, thoroughly, thoroughly, needed this!! Thanks!

Reply
Jon McBee
5/19/2016 08:46:56 am

Hi Paul,

I am glad that you found this post useful! Let me know if there are any other posts that you thoroughly need, I am always looking for new ideas to blog about.

-Jon

Reply
Shawn Hsiao
3/6/2019 10:15:39 am

Thanks for your sharing, it really help me a lot.

Reply



Leave a Reply.

    RSS Feed

    Tags

    All
    Abstract Messaging
    Actor Framework
    Agile
    AI
    Asynchronous Programming
    Best Practices
    C#
    Complexity
    Continuations
    Control Values By Index
    Craftsmanship
    Dependency Inversion
    Dependency Viewer
    Futures/Promises
    Genetic Algorithm
    Liskov Substitution
    Malleable VIs
    Mediator Pattern
    .NET
    Object Oriented
    Object-Oriented
    Packed Project Library
    Parallelism
    Polymorphism
    Pub/Sub
    RawCap
    Root Loop
    Scrum
    Task Parallel Library
    TCP/IP
    TDD
    Test Engineering
    UML
    Unit Test
    VI Scripting
    VI Server
    WireShark

    Archives

    April 2019
    July 2018
    October 2017
    March 2017
    February 2017
    January 2017
    December 2016
    July 2016
    June 2016
    May 2016
    April 2016
    March 2016
    February 2016
    January 2016
    December 2015
    November 2015
    October 2015
    August 2015
    February 2015
    January 2015

    LabVIEW Blogs

    Eyes on VIs
    LabVIEW Hacker
    VI Shots
    LabVIEW Artisan
    Software Engineering for LabVIEW
    ​Wiresmith Techology
Picture
Powered by the Panda Ant

  • Blog
  • About
  • Contact
  • Tools
  • Resources