02-13-2022 06:34 PM
That always struck me as odd about AF. It feels you are exposing the underlying communication mechanism. Sure you've wrapped it in an enqueuer, but the name enqueuer kind if implies there is a queue. It would be nice to send a message and not really care about the delivery method.
DQMH hides the mechanism for requests. They are events, but the end user has no idea. For the broadcasts it does expose the events ref, so it's "half" as clean.
02-14-2022 08:16 AM
@Taggart wrote:
Sure you've wrapped it in an enqueuer, but the name enqueuer kind if implies there is a queue. It would be nice to send a message and not really care about the delivery method.
It does more than imply there is a queue. It explicitly says that there is a queue specifically so that you know the FIFO behavior. It doesn't say anything about the implementation of this queue. There may or may not be a Queue data type under the hood, but the abstract data type is a queue. In fact, there isn't a singular queue under the hood, there are four, and they can be moderated by network connections... the implementation is not exposed at all and can be replaced at any time, but it must maintain the queue semantics. This is a rather important promise to make to all users of the AF and is thus part of the public signature.
02-14-2022 08:50 AM
@JimB. wrote:I feel like there is a layer missing. That within a given actor's lvlib, both the messages and the actor itself should be private. The public class should be a wrapper that exposes each message for that actor as a method of that class.
You're not wrong, but you're not right either. The middle layer that you speak of would be a great idea, but we cannot build it. It falls afoul of the same type calculus problem that I described in the other thread. Basically, you can create the middle layer, but if you use the middle layer, you cannot inherit from the class... your caller is then locked to that specific nested actor, not any of the children. That cuts off design goal #2 of the AF, which was reuse of the code for new actors. (Goal #1 was a design that was safe from the four bugs that plagued other designs for lifetime, deadlock, echo chamber, and reliable order of messages in multi-hop delivery.)
02-14-2022 02:26 PM
@AristosQueue (NI) wrote:
@JimB. wrote:I feel like there is a layer missing. That within a given actor's lvlib, both the messages and the actor itself should be private. The public class should be a wrapper that exposes each message for that actor as a method of that class.
You're not wrong, but you're not right either. The middle layer that you speak of would be a great idea, but we cannot build it. It falls afoul of the same type calculus problem that I described in the other thread. Basically, you can create the middle layer, but if you use the middle layer, you cannot inherit from the class... your caller is then locked to that specific nested actor, not any of the children. That cuts off design goal #2 of the AF, which was reuse of the code for new actors. (Goal #1 was a design that was safe from the four bugs that plagued other designs for lifetime, deadlock, echo chamber, and reliable order of messages in multi-hop delivery.)
I believe what you are describing here is the inheritance issue I outlined later in my comment, no?
You would be able to inherit from the middle layer, but the issue with making the actor class private would be having to reimplement a new actor in every concrete class. Yes, this is quite detrimental to reuse, and community scope is a mediocre (at best) work around.
I thought about it a bit more and realized that perhaps an alternative would be to keep the AF methods and wrapper methods in the same class, but place the AF methods in protected scope so external callers could not access them directly... But just a little more thought is definitely uncovering some code smells. The fact that the private data would contain elements relevant to both the wrapper methods and the actor methods and the actor would have a separate copy would make things somewhat confusing for the developer of such a class and definitely screams SRP violation.
Dropping the idea of making the actor private and just not using it directly in calling code doesn't really enforce proper usage, but might turn out to be the least hassle. Might also open the door to some slight variations on my original idea that are now rattling around in my head.
I know I already mentioned it, but the only good solution I can think of for the architecture I originally envisioned is with classes inside classes, thus allowing for protected internal classes. Of course, that requires a change to LabVIEW that I assume is probably unlikely to happen.
02-14-2022 02:51 PM
Question: could you not just create a class that is the API for your actor? You might refer to it as a proxy pattern. It could hold onto the enqueuer and have a method for sending each message that just delegates to the appropriate send method? and if you have a child of that actor, just create a wrapper for that child that inherits from the wrapper for the parent. Then instead of passing around the enqueuer, you pass around this proxy object?
Would that solve your problem?
02-14-2022 05:14 PM
I don't actually have a problem. Just pontificating on some architectural thoughts. 😉 Actually, I apologize to the OP if I have totally derailed their topic.
And yes, that's basically what I've been trying to describe. Sorry if it wasn't clear. Most of the stumbling points in this mental exercise were around the initial vision of restricting usage of the underlying actor by setting it to some form of protected scope. That throws a monkey wrench into the inheritance structures possible in LabVIEW.
02-14-2022 06:19 PM
@JimB. wrote:I know I already mentioned it, but the only good solution I can think of for the architecture I originally envisioned is with classes inside classes, thus allowing for protected internal classes. Of course, that requires a change to LabVIEW that I assume is probably unlikely to happen.
You might be right, but I'm pretty certain I ruled out that solution working for some reason. Over the years, I've tried many attempts on this problem. It's something I've spent cumulative weeks stewing about, and I know I looked in that direction.
If that solution does work, internal classes have enough conceptual oddities around them that the work involved would be in the "large project" category, and if I'm going that far, I'd rather spend my time on native actors with a better type calculus so the interim layers aren't needed.
02-14-2022 07:06 PM
I guess my question is why use inheritance? Instead of inheriting from Enqueuer, why not use composition and wrap the enqueuer?
02-14-2022 07:17 PM
@Taggart wrote:
I guess my question is why use inheritance? Instead of inheriting from Enqueuer, why not use composition and wrap the enqueuer?
Why use inheritance for what? For actors? Nothing inherits from Enqueuer.
02-14-2022 07:30 PM
@AristosQueue (NI) wrote:
@Taggart wrote:
I guess my question is why use inheritance? Instead of inheriting from Enqueuer, why not use composition and wrap the enqueuer?
Why use inheritance for what? For actors? Nothing inherits from Enqueuer.
I was responding to:
And yes, that's basically what I've been trying to describe. Sorry if it wasn't clear. Most of the stumbling points in this mental exercise were around the initial vision of restricting usage of the underlying actor by setting it to some form of protected scope. That throws a monkey wrench into the inheritance structures possible in LabVIEW.