Actor Framework Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

Do Interfaces *completely* replace the Abstract/Concrete message paradigm?

I'm trying to update one of my data acquisition actors to use interfaces. It's also my first experience with interfaces in general, so I'm still wrapping my head around the new paradigm.

 

Short version: The old method provides an abstract class from which I can inherit multiple times. The new method provides an interface which I can implement once. What if I want the Caller class to provide multiple concrete messages to my Actor, instead of just one override?

 

Long version: As I understand the Interface paradigm:

 

-Interface.lvclass provides template methods and messages and contains a DoSomeStuff.vi, Send Message.vi, and a Do.vi.

 

-The "Callee" actor (the general-purpose one that does real work, and that other actors will call) will "do some work" then send a message to its caller using the Interface's Send Message.vi.

 

-The "Caller" actor (i.e., do a specific task that requires the Callee) inherits from Interface and overrides DoSomeStuff.vi. At runtime, it will launch the Callee actor, which will send back the Interface message containing Do.vi, then DoSomeStuff.vi will be overridden by the "specific task" actor to accomplish the task.

 

I think I get all of that. The Interface replaces the older paradigm of having the Callee actor provide an abstract message for the Caller actor to inherit from.

 

So, all that said, here's my question: does the Interface paradigm *completely* encompass the older paradigm? Because in my application, I have an actor that can implement the "DoSomeStuff.vi" multiple times. Here's the overview:

 

DAQInputActor can accept "data acquisition groups" that contains one or more data acquisition channels with various terminal configurations, scale factors, etc. as well as a Concrete Message. The actor accepts an array of these messages. Timing is shared across all of the data acquisition groups.

 

The purpose of the groups is that when an acquisition is complete, the DAQInputActor can send the data to any number of concrete classes implemented by its caller. For example, I can have a "Position sensors" group, a "Temperature sensors" group, and a "Illuminance sensors" group. From the DAQ's perspective, it just grabs data as usual, then splits it by message group to each of the three messages.

 

The Callee actor can then have three different messages, each of which overrides the "Send Data" message. I can then have dedicated messages to handle Position, Temperature, or Illuminance data.

 

I don't see a way to do this with the Interface method, as it will provide a single DoSomeStuff.vi to override which my Caller class can override just once, instead of providing an abstract class that I can inherit from multiple times.

 

Thus my question, can I do this with Interfaces, or should I stick with the Abstract/Concrete paradigm?

Message 1 of 7
(2,771 Views)

If I'm following you correctly, your nested actor (the "Callee") has three different data payload types, but each type has the same structure.  You want your caller actor to respond to each payload type differently, so you need three messages.  Because the three types have the same structure, you use the same abstract message for all three types, and rely on the caller to provide the the specific messages.  Is that a good summary?

 

Before interfaces, we only had abstract messages, and using the same abstract parent for all three messages was a legitimate way to reduce message bloat.  I've done it, and I've recommended it, because it works.

 

Now that we have interfaces, the landscape changes.  Interfaces won't let you do this trick, because there is no abstract message at all.  Your caller just has the three messages it is coded to receive, and your nested knows to send those three messages.

 

I think this is the better solution, because I think it is more actor-like.  Each message you send to an actor is an announcement of a change in the environment.  Your caller accepts three messages because its environment can change in three distinct ways, and you expect your caller to do something different for each type of change.  Your nested actor also knows this as well; it knows that it needs to make a different announcement (and thus send a different message) for each type of data.  The three messages happen to use the same data type, but that is coincidence.  The important thing is that you expect something different to happen for each type, so a distinct message for each type is the right answer.

 

Robert C Martin talks about the common mistake of assuming an inheritance structure because two things that are really pretty different happen to look the same.  You can cause yourself some real problems down the road.  Consider the scenario where you have to add a unique piece of data to, say, just the position announcement.  You would have to make a new abstract message class for just the position message, and then change the caller's child implementation as well.  If you then had to do the same thing with the temperature data, you'd have to do that work again.  What appeared to be three variants of a common message type are now obviously three unrelated message types.  But in truth, they always were different.

 

The interface model just starts with that (correct) assumption.

 

The intent of a message matters as much, or more, than the structure of the message payload.

 

So, going forward, I would definitely drop abstracts in this instance.

 

That said, going to your original question, abstract messages are still supported.  Indeed, even if we took out the tooling, you could still write them by hand.  There may be a few corner cases where they might be the preferred solution, though, to be honest, I can't think of one.

 

I'm not wholesale refactoring my abstract messages in my existing projects.  They work.  But if I need to refactor an abstract/concrete pair, I will most likely be replacing them with a message in an interface.

 

Message 2 of 7
(2,739 Views)

Thanks for the insight. Yes, you're right about my architecture.

 

Basically, the Callee actor just does n-channel DAQ sampling, and it sends this data back to its caller. Though the data itself may describe totally separate things, it has to work together due to hardware limitations. To somewhat steal your phrasing, though they may look like different things (various sensor values), to the Callee actor they are the same thing; just a bunch of voltages on a DAQ terminal.

 

Since this actor is designed for any application that needs continuous DAQ monitoring, I don't want to have its Interface describe the way the data is used- hence the abstract message.

 

So then, if I was to rescript this with Interfaces, I could see it going a couple ways:

1- Combine all sampled data into a single "New Data" message, pick array elements out of the returned data, and send them to various methods in my Caller actor. This is similar to what's going on now, except the definition of the data destinations is done once at the setup of the actor rather than "twice" with this method. The original method directly ties channel setup with message destination, whereas this method requires me to set up the channels in the right order, then split the messages apart in that same order when data is returned before passing the data to each method.

2- Create another Actor (maybe a child of my "Generic DAQmx" Actor, call it "Specific Program" actor) that handles the above, letting this child actor split the Generic "New data" message into separate "New Position Data", "New Temperature Data", etc. messages. This seems like a bit more work but it does decouple responsibilities a bit, which is nice.

 

I definitely don't want my DAQ actor to know how to send more than one message, as the number of messages is unique to the caller application. If it can send three messages, then what if next time I have four channels, or five? I guess this lends itself to #2.

 

I do have to say that I feel like I don't gain much by moving to either of those paradigms. I do suppose you have a point about data manipulation... this way, I separate the responsibilities into "Sample data", "Sort data", and "Process specific data subset" into three different, decoupled actors.

0 Kudos
Message 3 of 7
(2,724 Views)

Do Interfaces *completely* replace the Abstract/Concrete message paradigm?

 

YES. 

JustACS has more details, but I’ve been working on exactly this question for a while now and I felt the thread needed an unequivocal YES. If we had had interfaces from the beginning, abstract messages never would have existed. 

Message 4 of 7
(2,693 Views)

Hi all,

I'd like to extend the question to: Do Interfaces add features to the abstract message paradigm?
My case is : I have a reusable module that uses abs msges to notify its caller. This works well. Should I (why would I) switch to the Interfaces paradigm?


For the exercise, we tried to, and the result is:
- it works well
- there is a few less classes (as there is less messages defined)
- it seems a little easier to use (less headhacks when creating a new actor using the module as a nested actor)
BUT
- i don't feel we gained that much.


Do you recommand (when it's possible) to switch to the interface paradigm and why?

0 Kudos
Message 5 of 7
(2,631 Views)

@Jonzarwal wrote:

Should I (why would I) switch to the Interfaces paradigm?


For the exercise, we tried to, and the result is:
- it works well
- there is a few less classes (as there is less messages defined)
- it seems a little easier to use (less headhacks when creating a new actor using the module as a nested actor)
BUT
- i don't feel we gained that much.

 


Looks like 3 good reasons to me! But maybe you value your sanity less highly 😉

 

I guess it depends if you'll ever look at the code otherwise - you said it's working well and so I suppose your question is really, should you put in work on something that's finished. If it's not never going to be seen again, probably good to change, but you could wait until you actually need to look at it (rather than proactively hunting down abstract messages).

 

That being said, fewer classes might give you a nicer load time and better IDE experience, but I haven't seen it be a problem for small numbers of classes. If you have lots of libraries loaded all at once and few PPLs to reduce the burden etc, might be more critical?


GCentral
0 Kudos
Message 6 of 7
(2,626 Views)

@Jonzarwal wrote:

Hi all,

I'd like to extend the question to: Do Interfaces add features to the abstract message paradigm?
My case is : I have a reusable module that uses abs msges to notify its caller. This works well. Should I (why would I) switch to the Interfaces paradigm?

 


I would not invest time refactoring working code to remove abstract messages unless I had some other reason to change the messaging between an actor and its caller, and I'd think hard before using both interfaces and abstracts in a given caller-nested pair.  They're a little fiddly, but abstract messages are perfectly functional.

 

That said, interfaces are the way to go moving forward if you want decoupled actors.  Other than private messages actors send only to themselves, I don't believe I will ever write a message for an actor again.

 

Right now, to properly decouple your actors, you have to factor your messages into your inheritance structure.  If I want a caller to be able to call any of several actors, those actors have to inherit a set of common messages - including the abstracts they send to the caller.  This is incredibly limiting if I want to inherit functionality instead.

 

Consider a plug-in architecture.  I want my caller to be able to launch an actor from a given set of actors.  To make it work, all of those actors must share a message set.  Prior to interfaces, we solved this by making a parent actor that provides that message set, and then making all of the plug-ins inherit from that parent.  I can have several such plug-ins, My caller can support more than one plug-in type, each with its own message set.  So far so good.  But what if some (but not all) of the actors in my first plug-in group type share common functionality with some (but not all) of the actors in the second group plug-in type?  I'm faced with a tough decision:  do I inherit for messages or for functionality?

 

With interfaces, I write actors that inherit functionality and implement messages, and life gets much simpler.

 

Short version:  if it ain't broke, don't fix it.  Do start using interfaces on new code, and where it makes sense when revisiting existing code.  Your abstract messages will keep doing their job until you need to make a change for some other reason.

 

(Edited for clarity)

0 Kudos
Message 7 of 7
(2,610 Views)