mje Posted August 23, 2011 Report Share Posted August 23, 2011 I'm wondering what some of you have settled on as best practice for defining a class' interface when the main use of a class will be through a DVR to that class. Specifically I'm referring to the dynamic interface, that is the collection of member VIs which are dynamically dispatched. For those who haven't really explored class DVRs, the caveat is that all DVR refnums are strictly typed*. If I have a VI with an in-out set of terminals of type DVR<Type=Parent.lvclass>, it is a static interface. When I then create Child.lvclass, I can't override this VI with a new one having terminals DVR<Type=Child.lvclass>. If the user is only ever handed a DVR to one of these classes, we really only have two options as I see it. Option 1: Design the class entirely by value This leaves the onus on the user to have to wrap every call (or chain of calls) up in an IPE: The good news is this allows exact control over when you're locking down access to the refnum. For this reason alone I think this is the way to go, as the inherent synchronization this allows is a fundamental guard for data integrity if that DVR is floating around several different parallel tasks. The bad news is this way of doing things is ugly as sin, it can quickly become a mess of Case+IPE structures scattered throughout the diagram. Option 2: Create wrappers for the public API The idea is here we have a static VI which wraps access to each public dynamic VI: Usage of such a class looks something like this: OK, using the DVR is now much cleaner, but there's a few caveats: Lack of synchronization is in my opinion the biggest. There's an inherent lock/release involved with each call to a wrapper method. There's absolutely no guarantee that some other process won't operate on the DVR between the call to WrapperA and WrapperB. I'd have to add extra layer of synchronization to allow this, and to be honest, I already had one by virtue of the DVR. This is the fundamental reason I've abandoned this type of pattern. Though as long as I keep the by-value interface public, nothing would stop me from using a Case+IPE when I need to enforce an atom of operation... Another problem is extending the API with a child class becomes ambiguous. Maybe I want to add a new public dynamic method. What do I do? I can create a wrapper method, and have it operate on a DVR<Type=Parent.lvclass>, but this is an absolute nightmare as far as synchronization and type safety goes: You could also create the method typed to a Child DVR, but that really only changes where the cast is placed, and besides, if you're trying to insert the wrapper VI in a call chain with an API defined across multiple levels of inheritance, it becomes really messy and cumbersome. Learned this one through experience. Has anyone come up with a better "pattern" (for lack of a better word) when dealing with DVR based class interfaces? Experience has taught me that Option 2 is just plain broken. Option 1, although ugly and often cumbersome, is perfectly functional. Attached is a sample project (LV2010SP1). Regards, -michael DVR Interface Example.zip *I realize there's a whole other discussion about what could happen if DVRs weren't statically typed, or if we had another type of terminal which adapted to type, or if pigs could fly. But the reality here is right now we have static DVR refnums, so let's please restrict ourselves to how we work with them, not what could be. Quote Link to comment
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.