Actor Framework Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

The Decorator pattern and forwarding messages

Solved!
Go to solution

I have an interface/actor that's an interactive GUI to configure a test(AbstractConfigurer.lvclass and SpecificConfigurer.lvclass). Normally, this UI is loaded into a subpanel of the main test actor. When the user interacts with it and changes a setting, it sends a message to its caller about the new setting. Currently it's tightly coupled to the main test actor since the configurer is not generic; it's for a specific actor (i.e., it calls hardcoded messages intended for the main test actor).

 

I have a new use case where I'd like to reuse 99% of the configurer, but I need to add a couple more settings for one specific test case. Most of the interactive logic will remain the same, so I don't want to reproduce all of it. Thus, I'd like to composite the Actor Cores of two different actors.

 

I'm using MGI's Panel Manager toolkit (which is wonderful!), but it only loads the most specific actor's Actor Core into the specified panel. (Yes, it offers tiered panels, but they require the parent to load a child- not a child to load a parent).

 

I don't want to edit SpecificConfigurer to add a subpanel for optional child classes to add things, so I am planning on using the Decorator pattern to accomplish my goal. It's my first time trying this pattern with GUI actors.

 

Basically I will now have SpecificConfigurerDecorator, which is a child of AbstractConfigurer. SpecificConfigurerDecorator will contain (by composition) SpecificConfigurer, and will provide an Actor Core override that contains the two new settings plus a subpanel. Upon launching SpecificConfigurerDecorator, it will launch SpecificConfigurer and put it inside its subpanel.

 

This is all fine, but I'm worried about message handling. Right now, SpecificConfigurer just sends its messages upstream to its caller, which works great... but if it's inside a decorator, then its caller is now SpecificConfigurerDecorator, not the main test program. The Decorator can't handle messages intended for the main test program.

 

My initial thought is to override SpecificConfigurerDecorator.lvclass::Receive Message, and handle error 1448 (error in To More Specific Class) by forwarding it to the decorator's caller. This definitely works (in my mockup at least), but I feel weird about blindly forwarding messages upstream. Is this the correct way to handle this?

 

I can handle messages the other direction (from the main test program TO the configurer) with very simple wrapper methods, since I know ahead of time what messages a Configurer can expect to receive, but I'm worried that blindly forwarding messages will bite me someday.

 

The only other idea I had is to let SpecificConfigurerDecorator implement the message handling interface of the main test actor, again providing simple wrapper methods that just forward messages to the Decorator's caller, but this seems like a lot of boilerplate code for little benefit. Is it cool to blindly forward messages, or should I explicitly handle the messages I expect to receive with trivial message forwarding methods?

0 Kudos
Message 1 of 8
(1,589 Views)

@BertMcMahan wrote:

The only other idea I had is to let SpecificConfigurerDecorator implement the message handling interface of the main test actor, again providing simple wrapper methods that just forward messages to the Decorator's caller, but this seems like a lot of boilerplate code for little benefit.


This is how I would do it. Yeah, it's boilerplate, but you could put it in a BaseConfigurerDecorator that all SpecificConfigurerDecorator classes inherit from so that you only have to write it once (if you anticipate having multiple decorator implementations). Personally, the thought of trying to run down a 1448 error due to a misdirected message that was then blindly forwarded on to another actor is enough motivation to go with this option.

0 Kudos
Message 2 of 8
(1,577 Views)
Solution
Accepted by topic author BertMcMahan

I helped designed a utility to handle this exact use case (amongst others) for Zyah Solutions. Here's a link to it VIPM.io and here's a blog post about it. Self promotion aside, it might be overkill for your situation anyway, but I'd be happy to talk more about it if seems interesting to you.

 

Alternatively, @justACS had an idea that he shared with us which is simpler and might work well for you in this instance. The catch is that you have to be using a version of LabVIEW that supports interfaces. But basically, the idea is that you have an interface with the payload methods and the default behavior of the method is to cast the interface wire to an actor, and then get the caller enqueuer and re-send the message to the caller. (Correct me if I've misstated that, Allen.) Then when SpecificConfigurerDecorator receives the message (change its inheritance so that it inherits from the SpecificConfigurer outgoing interface) it will automatically forward that message up - no need to override Receive Message. The final destination of the Msg would need to override the functionality so it doesn't continue to send the Msg up the Actor tree. Make sense?

CLA CLED AF Guild
Message 3 of 8
(1,564 Views)
Solution
Accepted by topic author BertMcMahan

@CaseyM wrote:

(Correct me if I've misstated that, Allen.)


No, you got it.

 

I agree with the OP's intuition.  Overriding Receive Message.vi and blindly forwarding a message that generates a 1448 error is a bad plan.  You eat the cast time, and you will obfuscate instances where you are, in fact, doing the wrong thing.

 

And I like the overall decorator idea.

Message 4 of 8
(1,507 Views)

Thanks guys. I'll just bite the bullet and add the boilerplate code. In this case it shouldn't matter too much anyway. For some reason I didn't think default implementations were allowed, but that definitely makes things much easier.

0 Kudos
Message 5 of 8
(1,502 Views)

OK, I had to shift gears the last week and am just now getting back to this.

 

If I understood you correctly, I need to create an Interface for anything that can call AbstractConfigurer. I'm calling this interface "Configurer Host", and it provides a method and a message that can be sent to anything that can host a configurer. I switch my old Actor (the one that launches these Configurers) to inherit from Configurer Host. Now, all Configurers will send Configurer Host messages to their caller.

 

Now that I have ConfiguratorDecorator involved, it needs to be both a Configurer and a Configurer Host. So far, so good.

 

Next, I'll modify the default implementations of my Configurer Host interfaces to cast the caller to an actor, get its enqueuer, and re-package the message into a new Send (xyz) Message. Thus, if a Configurer Host receives a message that it doesn't override, it just sends it up the chain until something DOES implement that message.

 

In this way I'm not forwarding Message objects exactly; I'm unpacking and repacking it into a new message.

 

Is all of that correct?

 

If so, I'm running into an issue with the default implementation within my Configurer Host interface where I cast it to an Actor to get the Enqueuer. I used To More Specific Class to convert the Interface into an Actor, then tried to use Read Caller Enqueuer... but that VI is in Protected scope, meaning only a class that inherits from Actor.lvclass can call it.

 

Since this is an Interface, I can't change its inheritance to Actor. How do I therefore get the Caller enqueuer from within the default method of the Interface?

 

I could always ignore the default implementations, and require Must Override, then in my decorator class just manually implement this, but it sounds like this should be possible. Any ideas?

 

(I do wonder why Read Caller Enqueuer is in a Protected class- that's not immediately obvious to me.)

0 Kudos
Message 6 of 8
(1,363 Views)

I will admit that I never actually tried to implement the idea of casting to an Actor in an interface, but it looks like you are correct - I also get an error because the <Read Caller Enqueuer> method is protected. I'm not sure how to continue along this line without modifying Actor.lvclass given this.

 

I'm also curious why the method is protected. My guess is that this use case was never envisioned since interfaces didn't exist at the time the scope was defined. And back then, why would you want to make the method public? It would serve no purpose.

 

Anyway, I guess I'd just use the Zyah AF Msg Forwarding Utility then. 😛

CLA CLED AF Guild
0 Kudos
Message 7 of 8
(1,354 Views)

Re the original post:

 

"Decorator Pattern" involves the mental image of "decorating", or modifying an object.  Although technically a Caller is calling a decorator which then calls the object, we mentally think of this as the Caller directly calling the object (decorated).  That's why it's called "Decorator".

 

An example might be a "Coffee" object with various "Option" decorators.  Although we might really by calling Take_a_sip on a "Two Sugars" decorator, which calls "Decaf", which calls "Soy Milk", which calls the "Latte" object, we are mentally calling Take_a_sip on a Latte (that just happens to be decaf with soy milk and two sugars).  This works because all the decorators blindly forward the Take_a_sip call.

 

My point is that your intuition, that it wrong to blindly forward messages, is right in general but completely wrong for decorators, which are intended to intuitively be the exact opposite.

 

 

 

 

Message 8 of 8
(1,344 Views)