0

This question is for LabVIEW 2019. Highlighting that fact first because some of the "standard" object-oriented techniques like interfaces, type inference, template specialization, etc. aren't available to me.

I'm working in an established code base where there is a single base class that handles core functionality and many tiers of subclasses. Some are abstract subclasses that encapsulate core functionality for a particular family of products, and then of course there are the concrete subclasses.

One responsibility of the base class is to manage a scripting engine. Methods are named in a text file to build a queue and then the base class runs those commands by reference.

The problem I'm having is that the commands depend on the class type. For example, the abstract "inspection_station" class might have a command called "log_inspection_results" that is common to all inspection stations, but then a "microscope_inspection_station" would be the only class that has a "connect_to_microscope" command.

What's more, some commands are overridden; in this example the inspection_station might write to a text file by default, but the microscope inspection station overrides that functionality to write a PNG instead.

The problem is that it's the base class that's trying to execute the commands. This was originally handled by defining all commands at the base class level. The call is defined at the base class, but at runtime it's the concrete instance executing the command and so the active type and the resulting command specialization is automatically correct. This is great, but defining a bunch of virtual commands at the base class just pollutes the base class and adds a lot of boilerplate work to add a new command.

I tried changing it such that the subclasses were responsible for tracking the commands they had available and would provide a list to the base class. The base class would invoke the commands blindly by name, but then the problem was that the base class could only hand a reference to the base class - all the subclass-specific commands needed to be restructured to take a generic base class reference and had to downcast to get the subclass-specific class data.

I take frequent downcasting as a code smell, but I can't figure out a better architecture. I thought I could restructure things a bit and make the command execution virtual, with the concept that the concrete subclass could pass its own reference to any tier of parent/grandparent/etc. class, because a more specific type is always a kind of less specific type. Unfortunately LabVIEW is very picky when calling by reference and the inputs need to EXACTLY match the invoked method - I can't pass in a subclass where the method signature is expecting a parent class because the types don't exactly match.

I thought I could change the structure to a visitor pattern and move the commands to their own class that accept a reference to the existing class, but here again the base class is what's going to be invoking the commands. This means a restructured command class would have to accept only a reference to the base class and then would need to do a bunch to type comparisons to figure out how to downcast, and there again is the same downcasting problem I was trying to avoid, but even worse with the type comparisons.

I'm not sure if there's a pattern that I'm forgetting about or if this isn't really doable given the constraints LabVIEW is imposing.

    1 Answer 1

    4

    This sounds vaguely like the yo-yo problem. You're weeding through multiple implementations trying to figure out which ones apply. This happens if you don't stick to a single abstraction and single implementation. If your call drills through multiple implementations, especially through inheritance where your call can jump up and down the inheritance stack, it can get very confusing as you're forced to view multiple files to follow the flow.

    The solution is to flatten the stack. One abstraction that defines how to talk to this thing. One implementation (at a time) that defines what it will do when talked to.

    You don't need keyword interfaces, type inference, down casting, or template specialization to do any of that. You just have to respect the time of those who will read your code.

    5
    • I am the guy trying to read the code here - I'm the new employee trying to work with a well-established code base, and my upcoming task is to create a new concrete subclass and to create quite a few new commands to support that new extension. I'm trying to work within the existing framework, but I will take this advice to heart and consider what the architecture would look like if I were creating my own system from scratch (e.g., favoring composition over inheritance).
      – Chuck
      CommentedFeb 13 at 18:49
    • 4
      @Chuck oh I know you're the guy reading now. I've been in your shoes. I only ask you to remember this feeling when you decide you're done. Because there is always the next poor guy. Who might be me. Or you in 6 months.CommentedFeb 13 at 19:24
    • 1
      Your feedback was really helpful to reconsider the structure. I think the best bet is to split the class from concrete, monolithic collections of <stuff> to a more flexible test_bench that can have an array of test_equipment. Commands can then live with the equipment, I can make a builder class to configure the test bench software (and that too could potentially become user-configurable without rebuilding), etc. I was fixating on "how do I solve this within the existing framework" and your feedback helped me take a step back and consider whether the existing framework was workable as-is.
      – Chuck
      CommentedFeb 13 at 21:55
    • 1
      @Chuck all sounds good. Make sure you have tests. When restructuring it’s really nice to know that you haven’t changed the behavior when you didn’t mean to.CommentedFeb 14 at 0:27
    • consider ... architecture ... if I were creating my own system -> Yes. Yes you should. Me: existing Equals override in a deep & wide class structure would mean a cascade of heroic code changes. Get this book about DI in .NET Not using .NET? This is applicable to DI & architecture generally. "seams" (pg22) was perhaps THE key for me. "Implant surgery" was delightfully minimal; regression testing was limited and quick.
      – radarbob
      CommentedFeb 19 at 22:10

    Start asking to get answers

    Find the answer to your question by asking.

    Ask question

    Explore related questions

    See similar questions with these tags.