Tom Bress Posted September 20, 2009 Report Share Posted September 20, 2009 I'm trying to get up to speed with OO and I have a question. Here is the scenario: In a string-based state machine I have a long string constant representing my list of states, like a program with one state per line. I have a number of these "programs" in the code based on user input. I'm trying to convert this to an OO approach and am thinking of this string as an object called Program. Let's suppose I have three programs: Program1, Program2 and Shutdown. Here are two approaches I could take: 1. Create a Program class with a string control in the data. Then create three method vis: Write Program1, Write Program2, Write Shutdown. Each of these vis is a static dispatch vi that accepts a Program object and writes the correct program to the string control. The programs would be accessed by a single Read Program method vi in the Program class. If the programs need to be changed in the future you edit the corresponding Write vi. 2. Create a Program class with a string control in the data. Create child classes called Program1, Program2 and Shutdown. Set them to inherit from Program. Create a dynamic dispatch vi in the Program object called Read Program. Override the Read Program vi in the children classes and return the string constants that contain the programs. If the programs need to be changed in the future you edit the Read Program vis. Which is better? I think the second option is more in the spirit of OO programming, but is this the best way to implement it? It seems more complicated. Quote Link to comment
jcarmody Posted September 20, 2009 Report Share Posted September 20, 2009 Not having done anything serious with LabVIEW OOP myself I feel it fortuitous that I can comment here first! So, I'm thinking that the spirit of OO programming would call for one Program class that you would instantiate three times. This class would have the Write method you mentioned and you'd specify that one is for Program1, one for Program2 and another for Shutdown. No? Quote Link to comment
Daklu Posted September 20, 2009 Report Share Posted September 20, 2009 (edited) I'm trying to convert this to an OO approach Are you sure you want to convert your application into an OO approach? If your application is already working or you have deadlines for getting it working, I'd strongly recommend sticking with traditional LV programming. Learning OOP is a bit like being a kid at an amusement park; every time you turn around you see something better that you just gotta try out. (Usually because the new approach appears to solve a problem your current implementation can't address.) End result? You can easily spend a lot of time rewriting the same code over and over. If your paycheck depends on you producing working software, start learning OOP on side projects. (Like the CLD practice exams.) </soapbox> Now, to more directly answer your question... Which is better? I think the second option is more in the spirit of OO programming, but is this the best way to implement it? There are several rules of thumb for OO programming. One that applies to your particular question is the "Open/Closed Principle." This principle states that working code should be "open to extension but closed to modification." It is based on the idea that once you have working code you don't want to be mucking around in it and potentially introducting new bugs. In other words, if it ain't broke, don't fix it. Unfortunately there isn't enough information for me to suggest a "best" approach. It depends a lot on what can be changed, either by the user when running the application or by new application requirements. You mentioned Program1, Program2, and Shutdown. Do you also need to support the possibility of Program3, Program4, ..., ProgramN? Can the user change the program contents at runtime? Do you need more than one program object at any given time? Do you expect to be able to use your program class in other applications? Option 1 is the easiest to implement but as expected, offers the least flexibility. Users have to select the program from a predefined list. You have to edit your class source code to change a program or add new programs. Not only that, but when you add new programs (Write Program3, etc) you'll have to modify your application source code to use the new program. This clearly violates the Open/Closed Principle. Finally, this implementation makes it more difficult to reuse the Program class in other applications. Depending on your exact implementation, Option 2 doesn't offer any functional advantages over option 1 in the short term. Its benefit comes in code maintenance. It is a more standard design and I believe more easily understood by developers new to the project. It is also more easily refactored to extend your application's functionality. For example, you could relatively easily implement a plug-in architecture that allows you to create and distribute new Program child classes without modifying your application code base at all. (Trying to do that with option 1 requires you to modify your Program class, which is part of the application's code base.) Option J ( ), with its SetProgram method, allows for the possibility of having users define new programs at runtime. It also allows you to easily reuse the Program class; you don't have to change its source code at all to adapt it to other applications. A plug in architecture can be implemented that allows programs to be stored as text files, making it very easy to create new programs using notepad. It does not allow for an arbitrary number of simultaneous programs. (But then neither do the other options.) Perhaps ironically, I think it is also a simpler design than either option 1 or option 2. Whether or not it is the best option depends on your specific requirements. It seems more complicated. There's a good reason it seems more complicated. It is. IMO, object oriented programming generally does not reduce complexity of the application as a whole. What it does is encapsulate sections of complexity so you don't have to keep the entire application in your head to understand what is happening. Each layer of abstraction adds some amount of complexity, but once you have that working you can forget about the details and just know your Program class is working as expected. Keep in mind that as you become more familiar with OO patterns things that at one time seemed complex become much less so. Edited September 20, 2009 by Daklu Quote Link to comment
MikaelH Posted September 21, 2009 Report Share Posted September 21, 2009 Hi Count the number of attributes and methods you need in the class, then ask yourself does it make sense to create a class out of it. I wouldn't create classes if... ...you end up with a "tiny" class like this: ...you end up with a "property" class like this: Cheers, Mikael Quote Link to comment
Falevoz Y. Posted September 21, 2009 Report Share Posted September 21, 2009 Hi Count the number of attributes and methods you need in the class, then ask yourself does it make sense to create a class out of it. I wouldn't create classes if... ...you end up with a "tiny" class like this: ...you end up with a "property" class like this: Cheers, Mikael I agree that your lamp class is too simple to justify an OOP implementation. But, with not much more complexity, it can be justified by flexibility that it bring... Even more if the requirements are not so stable... Yann Quote Link to comment
PaulL Posted September 21, 2009 Report Share Posted September 21, 2009 Tom, I'm not too clear yet on exactly what you are trying to do. You mentioned a statemachine so at first I thought we should use the State Pattern (which we have done and is quite straightforward), but after rereading your description a few times it seems to me that you are executing a series of steps (basically a macro), choosing the macro based on user input. Is that correct? Then the problem you are trying to solve is how to send the proper macro to the controller. If that is the case, we can do that quite efficiently using the Command Pattern (we have done this with simple commands and it is even more straightforward; the pattern does support macros). Let me know if this is on the right track. If so, I think I can help you with this. Paul Quote Link to comment
jcarmody Posted September 22, 2009 Report Share Posted September 22, 2009 [...] executing a series of steps (basically a macro), choosing the macro based on user input. Is that correct? Then the problem you are trying to solve is how to send the proper macro to the controller. If that is the case, we can do that quite efficiently using the Command Pattern (we have done this with simple commands and it is even more straightforward; the pattern does support macros). Let me know if this is on the right track. If so, I think I can help you with this. Paul Paul, I've built an ATE that uses the JKI State Machine to do this. It will run through an entire test sequence, but I give the operator the option to run any individual test or a group of tests (I HATE ATEs that don't have this ability!) I'm curious about this "Command Pattern" of which you speak. Can you give some details? Thank you, Jim Quote Link to comment
PaulL Posted September 22, 2009 Report Share Posted September 22, 2009 Paul, I've built an ATE that uses the JKI State Machine to do this. It will run through an entire test sequence, but I give the operator the option to run any individual test or a group of tests (I HATE ATEs that don't have this ability!) I'm curious about this "Command Pattern" of which you speak. Can you give some details? Thank you, Jim Well, first I will mention that for full details you will want to check out the classic GoF Design Patterns book and the more conversational Head First Design Patterns book for all the details and more insight than I could ever offer. Nonetheless, the heart of the Command Pattern involves making commands into objects. So, for instance, we might have the following. The top-level Command class is an abstract class. (Really, it would be an interface but the closest thing in LabVIEW currently is an abstract class. The important thing is that we never instantiate a Command object.) It has a single method, execute(). The actual commands are concrete classes that inherit from Command. Each concrete command class implements the execute() method. Note that a particular command can contain attributes (i.e., command parameters, or more abstractly, state). I don't show in the diagram the Client (e.g., a UI, but multiple Clients can invoke the same command, so that we could have a UI Client and a Command Line Client), a Receiver (a component that knows what to do with the commands; again the Receiver is decoupled from the Command classes so the framework can support different Receivers), and an Invoker (determines when--may be immediately--to call the Command::execute() method). Note that the Invoker calls Command::execute; dynamic dispatching determines which execute() method actually, um, well, executes. Notes: One can assemble commands into composite commands (with the Composite Pattern) to implement macros. (Essentially a MacroCommand executes a sequence of Commands.) One can combine the Command and Memento patterns to support undo. One can easily log Commands (and read back the log!) simply by writing and reading the objects (e.g., as flattened strings) in a file, all by thinking of them at the code-level as instances of Command. Most importantly, again, the Command pattern decouples the Invoker and the Receiver! An example from our experience: The Command Pattern is precisely a pattern so many variations are possible. In one of our subsystems we send Commands from a UI or external subsystem. The Invoker is really just a message passing system (we send objects flattened to strings over a CommandSharedVariable) combined with a an event structure that fires upon receipt of a message and calls Command::execute. The Receiver is simply the Controller and it knows what to do with each Command. (Note that in our case how the Receiver responds to a particular command depends on its current state, so it implements the State Pattern as well.) 1 Quote Link to comment
Daklu Posted September 23, 2009 Report Share Posted September 23, 2009 I'm curious about this "Command Pattern" of which you speak. Can you give some details? Jim, I'm not sure if you noticed this but what you suggested in post #2 is essentially the command pattern. (Though Paul did a much better job of explaining it than either of us!) The only difference between what your suggestion and Paul's illustration is the level of resolution in the commands. Your commands encompass an entire program; Paul's commands are discrete. Either way works, it's just a matter of what the developer is trying to accomplish. Quote Link to comment
jcarmody Posted September 23, 2009 Report Share Posted September 23, 2009 Jim, I'm not sure if you noticed this but what you suggested in post #2 is essentially the command pattern. It was entirely accidental. But, I just ordered The Object-Oriented Thought Process so I can get up to speed with the crazy things some of you folks are talking about. Jim Quote Link to comment
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.