Actor Framework Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

Typedef clusters?

Hello there,

I'm wondering...If the message data/contents I am sending from one actor to another is a cluster, should that cluster always be a typedef? And if so who should 'own' the typedef?

An alternative would be not to use a cluster at all but it makes it difficult to wire up to the connector pane.

Thanks,

Martin

0 Kudos
Message 1 of 10
(7,358 Views)

...Why not an object?

Cheers,

Matt Pollock
National Instruments
0 Kudos
Message 2 of 10
(4,413 Views)

Because objects need an icon and methods? Because a class library shared between two projects (and maybe even two targets) gets locked for edits until one of them is unloaded? Because if you start creating hierarchies or encapsulating objects, every time you load one object into memory, you'll be loading several others along with it?

Message 3 of 10
(4,413 Views)

I was being a little facetious with my post, given the forum we're posting in.  Yes, there are numerous valid reasons why clusters are the right choice.  There are also reasons why an object can be the right choice - this seems to be the right kind of place to bring up that type of design decision.

As to Martin's actual question, I'm personally partial to making all of my cluster types that cross a VI boundary a typedef.  In this way, the editor can notify me that I'm doing something stupid if the type changes at a later time.

With regard to ownership, I tend to think that the actor who is doing the most with the data should be the owner.  For example, if this was a configuration message to an actor, the actor being configured should own the typedef.  Usually the receiver of the message would be the owner in my designs, but others may differ there.

Cheers,

Matt Pollock
National Instruments
Message 4 of 10
(4,413 Views)

I suggest keeping it outside of both of the classes or their libraries unless they are all in the same library.  You don't want dependancies between the classes, better to have multiple classes dependant on an outside control.

Casey Lamers


Phoenix, LLC


casey.lamers@phoenixwi.com


CLA, LabVIEW Champion


Check Out the Software Engineering Processes, Architecture, and Design track at NIWeek. 2018 I guarantee you will learn things you can use daily! I will be presenting!

Message 5 of 10
(4,413 Views)

In my projects, I generally include a typedef in a class only if that class is the only user of that typedef. If multiple classes handle that typedef, I create a separate library to contain all of the common typedefs for that project. This helps in situations where you want to create some sort of support application (config file editor, data viewer). It may be helpful to be able to create that application and make use of certain typedefs without depending on the entire project.

0 Kudos
Message 6 of 10
(4,413 Views)

Good point.  Most of my development with actors is a set of related actors and support classes that live in the same library.

Cheers,

Matt Pollock
National Instruments
0 Kudos
Message 7 of 10
(4,413 Views)

MartinMcD wrote:

...it makes it difficult to wire up to the connector pane

This might even be a false premise. I suggest API methods which expose N primitive datatypes fanned over N conpane terminals is a better API than 1 composite datatype (structure) on 1 terminal.

Reasoning? We shift the burden of constructing the data structure internal to that method. Developers using your API are More Happier to write less code.

More reasoning? Consider Setter Injection vs Constructor Injection (that link is just a google search -- eat your heart out). Typically, Constructor Injection is going to provide a better API.

Even More? Testability of the API.

(Of course, sometimes it's desirable/necessary for the caller to construct -- but generally, we try to avoid this extra burden on the caller. One notable exception is when Dynamic Dispatch or even Multiple Dispatch is desirable -- here, the caller must initially contruct the objects)

Below is a quick snippet that shows the difference between the two strategies for API design. Below, a method (represented by the Sequence Structure) performs a task, and it needs two objects to perform that task. Creating this API method, we expose either 1) those two objects in our function prototype or 2) the primitive fields that compose those two objects. Notice how the calling code is cleaner; additionally, try to consider why the bottom would allow powerful Constructor Injection semantics. (hint: inside the method, we may want to apply additional preconditions or other rulesets to the primitive types used to construct the composite object rather than allowing the caller to construct "whatever"):

API-Design-Code-Boundaries.png

Message 8 of 10
(4,413 Views)

How does Message Maker handle a large number of message data items? I haven't tried it with any more than two.

0 Kudos
Message 9 of 10
(4,413 Views)

MartinMcD wrote:

...should that cluster always be a typedef?

I generally try to avoid telling people what they "should" do, since it depends so heavily on goals or circumstances that have not been articulated.  However, in this case you asked if the cluster should "always" be a typedef, so I can confidently respond, "no."  Typedefs are a custom defined data type (as are classes) and any time you use a custom defined data type in code you are creating a static dependency to the code base where that data type is defined.  Sometimes that is beneficial, sometimes it is a headache.

When it comes to passing messages across libraries I fall in line with what Jack described.  95% of the time I structure my message data to only use native data types or data types from a common library.  This helps eliminate unwanted dependencies and simplifies development, testing, etc.

That said, the AF uses command-pattern messaging which tends to create tightly coupled code anyway, so you may not get much benefit from decoupling your data types.

MartinMcD wrote:

And if so who should 'own' the typedef?

(Insert disclaimer regarding "should.")

The owner is the actor/component responsible for the data.  In my code, I pay very close attention to the dependency graph and avoid circular dependencies as much as possible.  Higher level components can depend on lower level components, but not vice versa.  So for me it's easy--when using specialized custom data types the lower level component owns the data type.  That also means I build the data type so it's generally useful for any code that would call the lower level component rather than customized for this particular caller's needs. 

For example, suppose the low level component encapsulates an instrument and a higher level component needs some custom subset of all the data the instrument provides.  The instrument component cannot be responsible for knowing what specific subset of data an arbitrary caller is interested in, so the typedef shouldn't (according to the standards I follow) belong to that component.  The caller is responsible for assembling the data it wants from the methods and data types the instrument component exposes; therefore, the caller should own the typedef.

0 Kudos
Message 10 of 10
(4,413 Views)