Ton Plomp Posted August 4, 2009 Report Share Posted August 4, 2009 I am starting to use inheritance and am facing the following problem: I would like to inherit from 2 classes. Here is my project: I have a GPS class that reads out a GPS device, using one of two classes: USB and serial. For storing the data I have two classes as well: TDMS and CSV. Here is my current hierarchy: Is this doable with one class or should I create an 'Input' class for the USB and serial classes and a 'Store' class for the CSV and TDMS classes? Ton Quote Link to comment
jgcode Posted August 4, 2009 Report Share Posted August 4, 2009 (edited) I am starting to use inheritance and am facing the following problem: I would like to inherit from 2 classes. Ton Hi Ton I am fairly new to OOP but would still like to suggest the following from my current experiences: Using the verification statement for inheritance can help: Ask yourself, or jot down on paper, if the subclass (e.g USB) IS A more specific type of the superclass (e.g. GPS) USB is a GPS (makes sense from a DAQ-type POV) Serial is a GPS (makes sense from a DAQ-type POV) TDMS is a GPS (does not makes sense) CVS is a GPS (does not makes sense) Therefore TDMS and CVS should not inherit from GPS. You may want to create another superclass that these two classes inherit from - e.g. a Log Interface - and this class could be have a association with GPS (either through aggregation or composition relationship etc...). I have been told (in other posts) that design pattern knowledge is the way to go to improve class hierarchy design - this is something I am trying to achieve at the moment. I have had similar issues as you along the way learning this (as I am sure others have too). Its all part of the fun IMO (Endevo) Mike H mad a recently great post regarding Interfaces in LabVIEW - it is work checking out. And of course AQ's Design Patterns Document on ni.com. Edited August 4, 2009 by jgcode 1 Quote Link to comment
Aristos Queue Posted August 4, 2009 Report Share Posted August 4, 2009 Take a look at the Delegate pattern in the design patterns document. It's relevant to what you're asking. Quote Link to comment
shoneill Posted August 4, 2009 Report Share Posted August 4, 2009 Take a look at the Delegate pattern in the design patterns document. It's relevant to what you're asking. I was going to suggest the exact same thing. It really does go some way to compensate for not having multiple inheritance*. Shane. * for those who insist multiple inheritance is a good thing.. Quote Link to comment
Popular Post Daklu Posted August 5, 2009 Popular Post Report Share Posted August 5, 2009 (edited) Is this doable with one class or should I create an 'Input' class for the USB and serial classes and a 'Store' class for the CSV and TDMS classes? Ton, While the ideas of OO programming are pretty straightforward, actually designing a good OO architecture is quite difficult. Newcomers (and I include myself in this group) overuse inheritance, underuse abstract classes, and tend to develop a class hierarchy that is very difficult to modify and maintain. The kind of stuff you're trying to do is why I think getting interfaces in Labview is so important to LVOOP. (And why I keep talking about them.) The hierarchy you posted isn't going to work very well. The exact design depends a lot on the requirements of the application you are building--especially with respect to what changes might be required in the future. Here's a diagram I put together that I believe will give you more flexibility. It's just a starting point; modify it to fit your requirements. [Quick UML explanation for those unfamiliar with it] Each block represents one class with the top section containing the class name in bold. The middle section is the private data contained in the class. The bottom section lists the class methods (member VIs.) Italicised names indicate an abstract class (BaseGPS) or function (SaveData). Non-italicised names indicate a concrete implementation. The arrow with the hollow triangle indicates inheritance. (Garmin inherits from BaseGPS.) The line with the solid diamond indicates composition, meaning one class contains another class as private data. (The BaseGPS class has a BaseHWComm class as a member of its private data.) [/uML explanation] Notice the linking between the three different class sets is always through the abstract classes. This helps maintain modularity and flexibility. For example, if you need to implement wireless communication in a future product, you simply derive a new child class ("802.11g") from BaseHWComm, modify BaseHWComm:CreateNew.vi to accomodate the new class, and you're good to go. (Mostly... it depends on exactly how you use the HWComm functionality in BaseGPS.) Also note that your coding is primarily done using abstract class methods, not the concrete class methods. For example, in your application all your GPS functionality is built using VIs from BaseGPS. In your Garmin and TomTom classes you use VIs from BasePersistData and BaseHWComm, not the child classes. There are drawbacks to this design: If you want to expose any of the methods from BasePersistData or BaseHWComm you essentially have to wrap a BaseGPS method around them. This is extra coding that in a perfect world wouldn't be necessary. If your class has a lot of classes as private data it can mean a lot extra VIs that clutter up the BaseGPS API. It can also potentially lead to naming confusion if more than one class uses the same name for member VIs. As it turns out, just this last weekend I prototyped a framework for LV8.6 that implements interfaces in a way that more closely matches other languages. I also have a LV2009 design on paper that comes even closer... I think it will work but I haven't implemented it yet. I'm planning on writing them up and posting it soon. USB is a GPS (makes sense from a DAQ-type POV) Serial is a GPS (makes sense from a DAQ-type POV) I don't think this makes sense from an OO point of view. USB and Serial are used by GPS, they are not subsets of GPS. I have been told (in other posts) that design pattern knowledge is the way to go to improve class hierarchy design - this is something I am trying to achieve at the moment. I found this link a while back and the articles really solidified my understanding of what my OO designs were missing. Especially the Dependency Inversion Principle and the Walking Through a UML Design case study. Take a look at the Delegate pattern in the design patterns document. It's relevant to what you're asking. In that document the delegate pattern says, "This pattern applies best when you have a dynamic VI inherited from some ancestor and two descendent classes want to override that dynamic VI with exactly the same implementation. Delegation helps you avoid writing the implementation twice, once for each of the two classes." This is the way I see the pattern described in the article. Delegating functionality is very common in OO architectures, but the Delegate Pattern described in the article addresses the specific problem of avoiding having to duplicate code in similar child classes. That seems to me to be a different problem than the one Ton described. I recognize that the diagram I put together could be considered a slightly more complicated version of the Delgate Pattern, but it's a mighty big leap from point A to point B for us noobs. (If you ever get around to revising that article I suggest putting in some simple UML diagrams to help explain the patterns.) Edited August 5, 2009 by Daklu 3 Quote Link to comment
shoneill Posted August 5, 2009 Report Share Posted August 5, 2009 (edited) Daklu, A delegate pattern can also be used when the class definitions are different. Following scenario would be useful for the problem at hand. Class A defines the overall functionality required (Loading, Saving, Getting GPS position and so on). Class A has, as a part of its Data a Class B which defines methods for a GPS hardware module which are used commonly. The actual IMPLEMENTATION of the hardware interfacing is most likely within a child of Class B. By using several different children of Class B (Class B.1, Class B.2) within Class A, we have essentially inherited the interface of the base class (Class B) with a different implementation (Class B.1, Class B.2 and so on). Class A can also have, as part of its data, a Class C which defined methods for loading and saving. The ACTUAL implementation is again found in a CHILD of Class C (Class C.1, Class C.2). Again we have the functionality within Class A of Class C with the actual implementation defined by the child classes of Class C. While it is not true multiple inheritance, the effects are similar. We can define two private data member classes (Class B and Class C) which essentially define out interface. Children of these classes can then override the implementation but the method of interacting with these classes (the Interface) is defined by the base class definition. Class A, instead of inheriting from multiple classes is essentially a COMPOSITE class. I have used this several times and find it a very powerful way to build modular code. Shane. Edited August 5, 2009 by shoneill Quote Link to comment
Daklu Posted August 5, 2009 Report Share Posted August 5, 2009 (edited) A delegate pattern can also be used when the class definitions are different. ... Class A has, as a part of its Data a Class B which defines methods for a GPS hardware module which are used commonly. The actual IMPLEMENTATION of the hardware interfacing is most likely within a child of Class B. By using several different children of Class B (Class B.1, Class B.2) within Class A, we have essentially inherited the interface of the base class (Class B) with a different implementation (Class B.1, Class B.2 and so on). Class A can also have, as part of its data, a Class C which defined methods for loading and saving. The ACTUAL implementation is again found in a CHILD of Class C (Class C.1, Class C.2). Again we have the functionality within Class A of Class C with the actual implementation defined by the child classes of Class C. If I'm understanding you correctly, what you're describing is the same as the first diagram in my post above, which I agree uses delegation. My comment to AQ was simply to point out that the pattern as described in the article doesn't directly address Ton's problem and it's hard for OOP noobs (especially LVOOP noobs, who often have little formal programming training) to see the various ways delegation can be used to solve problems. If the article were expanded a bit in its description of that pattern it would be much more helpful. If you look at the LV example referred to in the article it uses delegation in two classes with LV:Object as the common ancestor; however, the class that is delegated to is a concrete class, not an abstract base class. It's having the implementation in the delegate child classes that provides Ton with the flexibility he needs. Does pointing to an abstract base class justify a different pattern name? <*shrug*> I dunno. Several patterns are variations of other patterns, but I'll leave that question to those smarter than me. Dave [Edit] Class A, instead of inheriting from multiple classes is essentially a COMPOSITE class. How is a composite class different from a facade? Edited August 5, 2009 by Daklu Quote Link to comment
shoneill Posted August 5, 2009 Report Share Posted August 5, 2009 (edited) I wasn't sure if you were addressing a "proper" delegate or just the version shown in the documentation, hence my attempt at clarification. I'm not really up-to-speed on the different OOP patterns. I was using the Delegate pattern before I had ever heard of it, so I'm certainly no authority on the subject. I learn by doing and then some time later I learn how to call what I've been doing.... Differences to a Facade? My understanding (limited as it is) is that the primary role of a facade is to simplify things. A composite class doesn't necessarily do that. Although a facade is almost certainly also a composite class. That's about all I know (about OOP patterns). Shane. Ps Re-reading your post in light of your new post makes me realise you were actually expanding on the delegate idea instead of offering an alternative. Only realised that now. Edited August 5, 2009 by shoneill Quote Link to comment
Daklu Posted August 5, 2009 Report Share Posted August 5, 2009 I learn by doing and then some time later I learn how to call what I've been doing.... Me too. There's nothing quite like reinventing the wheel. The thrill of the discovery followed by the realization that not only has someone already invented it, but they did it better than you did. A composite class doesn't necessarily do that. Although a facade is almost certainly also a composite class. Good point. There seems to be a lot of subtleties in the jargon. I'm becoming more and more convinced OO design is something you have to learn by doing it and experiencing all the pitfalls associated with your design decisions. Learning the patterns certainly is necessary... learning which pattern (or pattern variation) to use in certain situations requires a deeper understanding of how the pattern implementation will affect your particular application. In any event, I'm glad Ton posted the question. I wish there was more OO design related discussion here. Quote Link to comment
shoneill Posted August 5, 2009 Report Share Posted August 5, 2009 Me too. There's nothing quite like reinventing the wheel. The thrill of the discovery followed by the realization that not only has someone already invented it, but they did it better than you did. Amen to that. It's getting over the disappointment the first few times that's hardest. I'm becoming more and more convinced OO design is something you have to learn by doing it and experiencing all the pitfalls associated with your design decisions. Learning the patterns certainly is necessary... learning which pattern (or pattern variation) to use in certain situations requires a deeper understanding of how the pattern implementation will affect your particular application. I was taught OOP in college. Well, it wasn't part of the curriculum in my course, but I read up on it anyway. I never ever grasped the idea behind OOP. I never understood how it was supposed to work until I realised that Inheritance and such were COMPILE time events. I had always envisaged changing inheritance at run-time and how this could possibly work really stumped me to the extent that I just gave up on it. With LV 8.20 I just implemented something and suddenly the lights went on. Something ridiculously silly to get stuck on but none of the resources I had been reading actually explicitly said it was a compile-time event. My assumption was just wrong and cost me about 10 years of OOP understanding. I believe the best jargon for that os D'oh! Shane. Quote Link to comment
Daklu Posted August 9, 2009 Report Share Posted August 9, 2009 (edited) Does pointing to an abstract base class justify a different pattern name? <*shrug*> I dunno. It turns out the answer is yes. The class diagram I drew up for Ton is a textbook Strategy Pattern. Apparently Strategy shares topology with the Bridge pattern, the difference being Bridge "allows you to vary the implementation and the abstraction by placing the two in separate class hierarchies." (Head First Design Patterns p.613) How does that distinction affect implementation? I have no idea. I'm willing to be enlightened if anyone has a clue. Something ridiculously silly to get stuck on but none of the resources I had been reading actually explicitly said it was a compile-time event. [cheek.insert(tongue)] You're thinking about this all wrong. You were subconciously applying the idea to an environment (run-time) the authors not only didn't intend, but didn't even anticipate that anyone would attempt, hence none of them ever bothered to explain it is a compile time event. You're thinking outside the box which is a sign of creativity and intelligence. (And ADD too. ) If it would have taken you 20 years to figure it out you would be super smart! Edited August 9, 2009 by Daklu 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.