Actor Framework Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

Actors and interfaces - am I doing it right?

Solved!
Go to solution

@Taggart wrote:

Why do you need a separate library for the interface? If Actor B sends some data to its caller Actor A, why not put the interface inside the same library as B? It's part of the API of B.

 


I did it that way just because I've seen it in the Actors and Interfaces example project, where Abstraction is a separate library just for the interface and messages. I've found a project I played around with about a year ago and I've placed the abstraction library inside the relevant actors' library as apparently back then it made more sense to me. That's why I want to see how others handle this.

0 Kudos
Message 11 of 37
(1,486 Views)
Solution
Accepted by horuable

It seems to work just fine and doesn't couple nested actor to its caller, but I guess it just feels a bit cumbersome to me to create a new library for every actor that has to send messages up the tree. Hence my question, am I doing this right, or is there some other approach I cannot think of myself?

AristosQueue and I went a few rounds on this topic when I was updating the AF course.  I was inclined to stick to the original model of one interface for all of A's messages.  After all, pre-interface, all of an actor's messages were grouped in one library.

 

I now realize that an interface is how one actor talks to another, for example, B talking to A, or C talking to A.

 

Assume B and C send different messages and there is only one interface to A.  Imagine I want to use B with an new actor, D, and D does not have C as a nested actor.  D would have to implement the same messages as A.  All of them.  That includes the messages C sends to A, even if those messages don't make sense for actor D.

 

So, even though it is just a bit cumbersome, how you are doing it is what we are recommending, and what we teach in the course.

 

(AristosQueue does point out some instances where he'd cut a few corners, or even forego interfaces in favor of standard messages.  I'm resistant, if only because my unit test strategy for actors relies very heavily on being able to isolate them.)

0 Kudos
Message 12 of 37
(1,484 Views)

@AristosQueue(NI) wrote:

B and C send different data with different types

 

If they send different data then you have two separate interfaces. My comments about a single interface only apply if B and C send the same thing. 

Of course, different data implies different interfaces, but the opposite is not true. Same data does not imply that they need to have the same interface. They can, but if the context is different, I would argue they should have seperate interfaces even if the data is the same.

Sam Taggart
CLA, CPI, CTD, LabVIEW Champion
DQMH Trusted Advisor
Read about my thoughts on Software Development at sasworkshops.com/blog
GCentral
0 Kudos
Message 13 of 37
(1,474 Views)

@justACS wrote:

@Taggart wrote:

Why do you need a separate library for the interface? If Actor B sends some data to its caller Actor A, why not put the interface inside the same library as B? It's part of the API of B.

 


If you do this, you can't ever use A in a system without B.  That's OK if A and B are that tightly coupled, but then there is very little benefit in using the interface at all.

 

The goal of using the interface is to allow A to be used with any actor that implements the B-to-A interface.  Putting the interface in the same library as B completely undermines that goal.


If you want to use A in a system without B, you would use a factory pattern, right? Let's call the parent class F.  Put the interface there then (in the same library as F) A needs to depend on something, right? So let it depend on F. Problem solved. You still don't need a separate library just for the interface.

Sam Taggart
CLA, CPI, CTD, LabVIEW Champion
DQMH Trusted Advisor
Read about my thoughts on Software Development at sasworkshops.com/blog
GCentral
0 Kudos
Message 14 of 37
(1,472 Views)

@AristosQueue(NI) wrote:

If they share some hierarchy (as an example if you are using the factory pattern), then keep the interface with the parent class.

 

The only hierarchy they would share in the factory IS the interface. There wouldn’t be a parent class at all. 🙂


Ah I think I see my misunderstanding now. Maybe?

Sam Taggart
CLA, CPI, CTD, LabVIEW Champion
DQMH Trusted Advisor
Read about my thoughts on Software Development at sasworkshops.com/blog
GCentral
0 Kudos
Message 15 of 37
(1,470 Views)

@justACS wrote:

@Taggart wrote:

Why do you need a separate library for the interface? If Actor B sends some data to its caller Actor A, why not put the interface inside the same library as B? It's part of the API of B.

 


If you do this, you can't ever use A in a system without B.  That's OK if A and B are that tightly coupled, but then there is very little benefit in using the interface at all.

 

The goal of using the interface is to allow A to be used with any actor that implements the B-to-A interface.  Putting the interface in the same library as B completely undermines that goal.


Ok, but if A (root) is already sending some messages to B (nested) without using interfaces, just normal messages from B's "Messages for this Actor" folder doesn't that alone make it so A cannot be used without B?

0 Kudos
Message 16 of 37
(1,432 Views)

This topic is a self-learning curve I've been going through. Great points overall. I lean towards what AQ and justACS are indicating. I too understand each interface to be cohesive behaviors packaged for re-use by (generally) unrelated classes. From that perspective alone, I like to keep interfaces and their associated messages separate from an actor's library. My actors tend to only have 'self-messages', which I tend to use for Time-Delayed Send Message type. I put outgoing messages from any actor generally into an xxxEvents interface (again adhering to the cohesiveness of the various interface methods in a single interface).

 

Consider a PowerSupply Actor and a Chiller Actor in the scheme of a durability tester. I may define a 'Connection.lvlib:Connection.lvclass' interface with three overridable methods: connect.vi, disconnect.vi and isConnected.vi. This interface will have the necessary Actor messages created within its library. I can now have both the above actors implement (inherit) the Connectable Interface. Notice that the Actor messages are created ONLY in the Connection library.

 

Similarly you can create a IPowerSupply.lvlib:IPowerSupply.lvclass interface that defines the standard PowerSupply methods: setOverVoltageLimit.vi, setOverCurrentLimit.vi, setOutputVoltage.vi, enable.vi, disable.vi, isEnabled.vi, etc. Again the Actor messages for this can be defined within this library. This interface would be implemented by only the PowerSupply actor (not the Chiller actor). Note that these are all messages sent by a caller to a PowerSupply actor.

 

For the outgoing messages (events) from PowerSupply, I could possibly define PowerSupplyEvents.lvlib:PowerSupplyEvents.lvclass interface with methods like: overCurrentLimitReached.vi, outputVoltageChanged.vi and outputCurrentChanged.vi (to provide new output voltage and current as measured by internal sensors). Now, these events would again only be relevant to the PowerSupply actor, but are NOT implemented by that actor. They would be implemented (inherited) by the caller actor that subscribes to these events.

 

What I've come to realize is that these events are not generally reusable. They tend to be domain specific. E.g. If I have two powersupplies in my tester, one for low voltage and the other for high voltage, I cannot re-use the events for both the power supplies if they are consumed by the same caller (say Tester.lvclass). Since Tester needs to differentiate between LowVoltagePS and HighVoltagePS, then the events have to be named accordingly. E.g. lowVoltageCurrentLimitReached.vi or highVoltageCurrentLimitReached.vi (I cannot reuse overCurrentLimitReached.vi as defined in the previous paragraph.)

 

Hope this makes sense.

0 Kudos
Message 17 of 37
(1,464 Views)

 


@justACS wrote:


If you do this, you can't ever use A in a system without B.  That's OK if A and B are that tightly coupled, but then there is very little benefit in using the interface at all.

 

The goal of using the interface is to allow A to be used with any actor that implements the B-to-A interface.  Putting the interface in the same library as B completely undermines that goal.


I feel like I need some class diagrams to understand this, but shouldn't that imply that the interface should be a part of A (as in inside the same library), since A is always going to depend on it (the interface) anyway?

Sam Taggart
CLA, CPI, CTD, LabVIEW Champion
DQMH Trusted Advisor
Read about my thoughts on Software Development at sasworkshops.com/blog
GCentral
0 Kudos
Message 18 of 37
(1,464 Views)

@Taggart wrote:

I feel like I need some class diagrams to understand this, but shouldn't that imply that the interface should be a part of A (as in inside the same library), since A is always going to depend on it (the interface) anyway?


I'm not sure if this is the same point as discussed in other posts in this thread (I feel like maybe not) but a reason to separate the interface for messages that A handles from the class A would be if you plan on having other classes implement that interface too...

 

Imagine instead that A.lvclass is a testing dummy class, and you can more easily imagine why you'd not need it in all users of A-interface.lvlib


GCentral
0 Kudos
Message 19 of 37
(1,453 Views)

@horuable wrote:


Ok, but if A (root) is already sending some messages to B (nested) without using interfaces, just normal messages from B's "Messages for this Actor" folder doesn't that alone make it so A cannot be used without B?


It does.  The interfaces to A still do not belong in B.  You may (for example) want to send some of those messages from another actor (A's caller, perhaps, or a new nested actor).  That new actor should not have to depend on B's library in any way.

 

Your current design has A tightly coupled to B, but not vice versa.  That's OK (but merely OK).  Coupling other things to B as well will complicate your project as it grows.  Builds, RT deployments, even loading the project suffer when there is too much coupling between actor libraries.  These things don't show up in a small actor system, but can really bite you later.

 

Personally, I don't even want A coupled to B.  I put an interface on B as well, and have A call the interface.  I have almost no coupling between my actors.  Even if all it does is make it easy to isolate actors for unit testing, I count it as worth my time.

0 Kudos
Message 20 of 37
(1,428 Views)