DQMH Consortium Toolkits Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

Is the DQMH module "tree" anti-dependency inversion principle?

I'm not a big OOP utilizer and also not formally educated in comp sci. But I do try my best to learn and be guided by certain proven concepts as we've all made horrible design decisions at some point in our early days that came back to bite us as the project grew or changed over time.

 

DQMH is by far my preferred way to program. I have spent a lot of time thinking about many aspects of it. Particularly the famous "DQMH do's and don'ts" blog post/guide.

 

One of the things that's always bothered me in an intellectual sense is that the tree architecture, where parent modules create new child modules and communicate with them by the request apis and receive messages via child broadcasts. This seems antithetical to the dependency inversion principle, to me, but I have a feeling I don't truly understand this principle. By using requests, isn't the higher level module dependent directly on the child module via statically linked and hard coded request vi's? I thought it was important for higher level layers to specifically NOT be dependent on lower layers, no? That said, I can't think of any way, in DQMH, for this not to be the case. New is glue, I was told.

 

How can one reconcile these two, seemingly opposing, ideas? 

0 Kudos
Message 1 of 6
(1,365 Views)

I had never heard of the dependency inversion principle before reading this. What it seems like to me, is that that principle will also ring true for packaged, re-use modules. The ones you intend on adding their public API to the palettes, like a device module. Two situations:

 

1. I have a SmartMotor DQMH module that has its public API added to the palettes via a VIPM package. If my SmartMotor module is calling a Request to some Error module to report an error, well then that's no good because now my SmartMotor module only works in conjunction with that error module. It will of course have a broken arrow if it can't locate that Error module.

 

2. I have a Controller DQMH module within a SmartMotor Control application. It calls these modules when it launches: GUI, Database, SmartMotor, Error Handler. This is not a generalized, re-use module that gets used across multiple projects, it is a custom Controller module built specifically for this application. 

 

The reconciliation, to me, seems to lie in whether or not you're talking about modules that get re-used across multiple projects, or modules that only ever reside within their own project. Presumably, just about every project you work will have unique Controller and GUI modules because those aren't things that can typically be generalized. The packaged re-use modules are the ones where you shouldn't usually see another layer underneath.

Redhawk
Test Engineer at Moog Inc.

Saying "Thanks that fixed it" or "Thanks that answers my question" and not giving a Kudo or Marked Solution, is like telling your waiter they did a great job and not leaving a tip. Please, tip your waiters.

0 Kudos
Message 2 of 6
(1,350 Views)

Hi, DQMH Modules do not follow any tree, you can use the PUBLIC API vis on any place, even by the module itself, the broadcast events are limited to be used by the vis inside the DQMH library vis because they are marked to be "private scope".

It is the responsibility of the DQMH Developer/designer to create the hierarchy of the modules (which module is the launcher of other modules).

There are some advantages of Broadcast events, you can subscribe to a broadcast event on any code or module without following any hierarchy. If you come from the Actor Framework world this probably seems odd but this is the way DQMH works.

 

When using the Actor framework there is the concept of Root Actor and Nested Actors, The root actor is the top level of the tree and it will launch nested actors, the nested actors can launch another actor, and so on, messaging between actors is done by sending the message to the caller actor or the nested actor, DQMH does not have this restriction and you can create circular dependencies easily.

 

I would suggest using Antidoc https://www.vipm.io/package/wovalab_lib_antidoc/ to reveal your DQMH Modules documentation and hierarchy. This is an amazing toolkit to help us understand DQMH projects. 

 

Cheers,

Enrique.

Message 3 of 6
(1,345 Views)

Thank you for using DQMH. It’s great that it is your preferred way!

 

Usually, the child modules are the more generic, reusable ones. Powersupply, DAQ, Logger… These are usually used by parent modules which are more specific: Maybe resembling a (part of) a test system, maybe a customer-specific UI, maybe a sequencer or state machine…

 

In this scenario it makes sense for the child modules not to depend on the project-specific parent modules so they can be reused across projects without pulling in unwanted dependencies. 

Also, seeing as the parent modules implement functionality specific to the child modules they use, it’s a given that the parents need the child modules to function. I.e. it’s ok for them to also depend on the child modules in the sense of linkage. 

I‘m not an OOP expert. My simple understanding of the Dependency Inversion Principle is that it introduces an abstract form of interaction between parent (high-level) and child (low-level). It is an advancement over the simple, standard ways of OOP with the aim of making high-level modules mire reusable. 

 

In my opinion and experience, the effort needed to implement the DIP doesn’t necessarily pay off unless you actually intend to reuse the parent modules as well. 

This is obviously a simplified and generic description. I‘d be happy to hear other opinions. 




DSH Pragmatic Software Development Workshops (Fab, Steve, Brian and me)
Release Automation Tools for LabVIEW (CI/CD integration with LabVIEW)
HSE Discord Server (Discuss our free and commercial tools and services)
DQMH® (The Future of Team-Based LabVIEW Development)


0 Kudos
Message 4 of 6
(1,343 Views)

@DoctorAutomatic wrote:

...also not formally educated in comp sci. But I do try my best to learn and be guided by certain proven concepts...


Ditto here. I haven't used DQMH yet. However, I do actively use OOP and recently embraced NI's Actor Framework (AF), primarily due to availability of native Interface classes since LabVIEW 2020.

 


@DoctorAutomatic wrote:

 

One of the things that's always bothered me in an intellectual sense is that the tree architecture, where parent modules create new child modules and communicate with them by the request apis and receive messages via child broadcasts. This seems antithetical to the dependency inversion principle, to me, but I have a feeling I don't truly understand this principle. By using requests, isn't the higher level module dependent directly on the child module via statically linked and hard coded request vi's? I thought it was important for higher level layers to specifically NOT be dependent on lower layers, no? That said, I can't think of any way, in DQMH, for this not to be the case. New is glue, I was told.

 

How can one reconcile these two, seemingly opposing, ideas? 


As Enrique alluded to in his post, I will use root, caller, or nested module (or actor in AF or node in a conventional tree structure) when referring to the tree-based communication architecture here. This is to not confuse with parent-child inheritance relationships in Class designs (even though it is otherwise common to refer to parent and child nodes in a tree structure).

 

You are well informed about 'new' being 'glue'. The key here is to perform the new operations of nested modules outside of the root or caller modules, based on your system's physical configuration (using a creational pattern). One would then inject these already-instantiated nested modules into the appropriate root or callers. After that, it is up to the application design when these nested modules will be 'launched' for the tree-based communication. Note, however, that there is no need for the root or caller module to know exactly what it is launching; as long as that nested module implements (or inherits from) a particular Interface or Abstract class definition. This last bit, is what I understand to be dependency inversion - both caller and nested modules communicate via the behavioral contract established by Interfaces or Abstract classes.

 

0 Kudos
Message 5 of 6
(1,315 Views)

@Dhakkan wrote:

@DoctorAutomatic wrote:

...also not formally educated in comp sci. But I do try my best to learn and be guided by certain proven concepts...


Ditto here. I haven't used DQMH yet. However, I do actively use OOP and recently embraced NI's Actor Framework (AF), primarily due to availability of native Interface classes since LabVIEW 2020.

 


@DoctorAutomatic wrote:

 

One of the things that's always bothered me in an intellectual sense is that the tree architecture, where parent modules create new child modules and communicate with them by the request apis and receive messages via child broadcasts. This seems antithetical to the dependency inversion principle, to me, but I have a feeling I don't truly understand this principle. By using requests, isn't the higher level module dependent directly on the child module via statically linked and hard coded request vi's? I thought it was important for higher level layers to specifically NOT be dependent on lower layers, no? That said, I can't think of any way, in DQMH, for this not to be the case. New is glue, I was told.

 

How can one reconcile these two, seemingly opposing, ideas? 


As Enrique alluded to in his post, I will use root, caller, or nested module (or actor in AF or node in a conventional tree structure) when referring to the tree-based communication architecture here. This is to not confuse with parent-child inheritance relationships in Class designs (even though it is otherwise common to refer to parent and child nodes in a tree structure).

 

You are well informed about 'new' being 'glue'. The key here is to perform the new operations of nested modules outside of the root or caller modules, based on your system's physical configuration (using a creational pattern). One would then inject these already-instantiated nested modules into the appropriate root or callers. After that, it is up to the application design when these nested modules will be 'launched' for the tree-based communication. Note, however, that there is no need for the root or caller module to know exactly what it is launching; as long as that nested module implements (or inherits from) a particular Interface or Abstract class definition. This last bit, is what I understand to be dependency inversion - both caller and nested modules communicate via the behavioral contract established by Interfaces or Abstract classes.

 


What you say is making sense to me. I'd be curious to hear from some DQMH experts on how one could implement "perform the new operations of nested modules outside of the root or caller modules". Would that be something like calling the start module vi's by reference and receiving some generic broadcast event ref from the newly started module in order to register for broadcasts? I'm having a sneaking suspicion that to do all this " injection" stuff one would definitely need to implement this using LVOOP. 

0 Kudos
Message 6 of 6
(1,298 Views)