*Use* class, or *Derive* class? (not a rant, its serious)
Hello everyone, I need a bit of advice on an OOP approach in the following situation. There''s been quite a few occasions where I bumped into this sort of problem. I could use any odd example for the sake of this discussion... Well, I''ll use one particular example just to provide the setup for one such situation. Here goes the situation setup, in 3 stages:
[stage 1] Suppose I write a Time Keeping class, which simply counts number of ticks either from the PC clock or number of cycles in main game loop. Method of counting is irrelevent. The important thing is that this class object acts as one "Stop Watch", which can be used for anything that needs some form of time/tick keeping. If more than one "Stop Watch" is required, you can keep making more objects out of this class and they will all work as independent Stop Watches. Say, this class would have member functions which performs Start, Stop and Reset, like the standard stop watch used for Olympics. Well, it should also have GetTimer() which returns the tick count so far. Lets call this Timer Class for now.
[stage 2] Now, suppose (we''ll hear this a lot) I write an Animation class which simply animates a "bob" at a given screen coordinate. This class simply handles a stationary animation. Say, this animation needs a Time Keeping system so it can switch frames appropriately, as set by the programmer. So, for convenience, I would derive this Animation Class from Timer Class. That would be
class Animation : public Timer
So far so good.
[stage 3] Having done that, suppose I want to make a class to Animate and Move (on screen) a set of frames. Call it Sprite Class. The obvious choice, then, is to derive this Sprite Class from Animation Class. That would be:
class Sprite : public Animation
Now, suppose this motion routine (which moves the bob around screen) also requires a timer to move the sprite, so it can move slowly or fast, or any sort of varying speed. (This is where the problem comes in... please read on).
What we now have is:
Timer class --> Animation class --> Sprite class
-*-*-*-*-*-*-*-*-
Now the setup of the situation is clear (I hope), here''s the problem.
Sprite Class requires TWO separate, independent "Stop Watches". One stop watch comes with Animation Class, alright. But that is used for animating the bob. There needs to be another timer to handle the motion of the bob. There seems to be a few work-around for this problem.
[] The obvious solution which comes to my mind is to USE the Timer Class in each of Animation Class and Sprite Class. So instead of deriving Animation Class from Timer Class, I would now create a Timer object in Animation Class. Likewise, when I derive Sprite Class from Animation Class, I would create yet another Timer object in Sprite Class.
This approach certainly solves this puzzle. But... no "inheritance" takes place between the Timer Class and Animation/Sprite class. If you want your whole design to be like MFC (i.e. what you want is a total encapsulation of all structures by pure OOP inheritance) then you might not like this solution.
[] Second solution which I would think of is to make the original Timer Class to handle multiple "Stop Watches" internally. This involves the actual modification of the base class. So now one Timer object could operate as 1, 2, 3 or any number of timers required, completely independently of each other.
The thing is, though, not only this would make it a considerably more complex class, I feel as if this Timer class is becoming rather "specialized" in its own way. In such a way that it was *designed* to be used for Sprite Class, rather than being blind of its derived classes (which is usually the case with inheritance - parent class would have no knowledge of child classes). I know that doesn''t mean we mustn''t do so. But what if I want a Timer class to be a single "Stop Watch" purely for the sake of simplicity and design? This solution, then, isn''t favourable. I understand that this is a "cosmetic" problem though.
[] Lastly, I can think of another (brutal) solution, which is to completely override the Timer Class when deriving child classes. To an extent that the shape and figure of the original class now is so blurred that you can''t even begin to guess what the original class might have been. lol. I don''t know how - I guess that depend on how you want it. But I guess one way is to override the way in which the time keeping variable is handled, so multiple thread of time keeping is done by reading off the original single-timer variable. Again, the detail of how this is done is irrelevent. The important thing is, now the Timer class is completely overriden inside out, in its functionality and shape.
This solution sort of makes me feel, "well, what was the point of making the base class Timer?". If I were to change the base class to such an extent, I might just as well not make the Timer class, but instead have the previously-derived class to do its own time keeping internally without derivation from Timer class.
-*-*-*-*-*-*-*-*-
I can think of quite a few other "solutions", such as deriving Sprite class as in the original setup, but make a Timer object in it for a separate "Stop Watch"... (but this is silly). All solutions I can think of are silly, and the above 3 are the better ones of them all.
I guess another plausible solution is to derive a second Timer class from the base Timer class, to handle complex time keeping... but then again, the original Timer class would just become one big (or small) virtual class, which makes it seem as if it were redundant... Might just as well make a complex Timer class in the first place.
How would you do this if you were me? I''m sure I''m missing something here... Object Oriented approach always has a sweet solution usually... which makes it feel so politically correct in its structural perfection. All the solutions I can think of, though, have some bitter end to it. As far as this particular sort of "puzzle" is concerned anyway.
Please share some thoughts on this if you can.
May 23, 2001 12:34 AM
simple: don''t use inheritance, you haven''t made a case for needing it. Inheritance exists to facilitate polymorphism, not to string objects together. I have a heart that keeps ticking, am I then descended from a heart? Is your computer just an extension to its mouse? If you have a sprite class that has a timer class as an ancestor, what happens when some other object uses a sprite as its timer? Is that something you want to be able to do? Even if you do you probably shouldn''t, and instead just give sprite a method to return a pointer to its timer or something like that.
There''s a distinction between the ''has-a'' relation and the ''is-a'' relation. In your example, an Animation clearly ''has-a'' Timer. This means that the Animation class has a Timer object as attribute.
Compare this with the ''is-a'' relation: a Cat ''is-an'' Animal, so Cat inherits from Animal.
HTH
Compare this with the ''is-a'' relation: a Cat ''is-an'' Animal, so Cat inherits from Animal.
HTH
Some useful C++ links:Free multiplatform ANSI C++ Standard Library implementationVisual C++ 6.0 STL fixesVisual C++ 6.0 noncompliance issuesC++ FAQ Lite
"But... no "inheritance" takes place between the Timer Class and Animation/Sprite class. If you want your whole design to be like MFC (i.e. what you want is a total encapsulation of all structures by pure OOP inheritance) then you might not like this solution."
For some reason, it feels like you''ve been confused by all these OOP ideas. You can have "total encapsulation" without using "pure OOP inheritance". They are not one and the same. Encapsulation is achived through protecting your data and defining a proper interface for your class. You can have encasulation without having any sort of inheritance relationship at all.
Furthermore, a "pure inheritance based" design as you described it is not good OOP practice. Inheritance is a very bad way to reuse the timer class. It lock the derived class into the behavior of the base class. Consider the scenario of having a few different implementations of your timer. Each new timer class might be a subclass of the original timer class. The only way for your Animation class to make use of any other kind of timer would be to change its base class. Can some Animations use one kind of timer while other make use of another? The reasoning can be applied to the Animation and Sprite class.
Anyway, I think the best solution here is the first solution you proposed, which is to use a "has-a" relationship instead of a "is-a" relationship. It makes more sense logically and will make your class library more flexible. An animation or a sprite is not really a timer, it simply makes use of one to achive their intended behavior. The Animation class would contain a pointer to a Timer. The Sprite class would contain a pointer to a Timer and a Animation. These 3 classes does not need to inherit from each other at all.
For some reason, it feels like you''ve been confused by all these OOP ideas. You can have "total encapsulation" without using "pure OOP inheritance". They are not one and the same. Encapsulation is achived through protecting your data and defining a proper interface for your class. You can have encasulation without having any sort of inheritance relationship at all.
Furthermore, a "pure inheritance based" design as you described it is not good OOP practice. Inheritance is a very bad way to reuse the timer class. It lock the derived class into the behavior of the base class. Consider the scenario of having a few different implementations of your timer. Each new timer class might be a subclass of the original timer class. The only way for your Animation class to make use of any other kind of timer would be to change its base class. Can some Animations use one kind of timer while other make use of another? The reasoning can be applied to the Animation and Sprite class.
Anyway, I think the best solution here is the first solution you proposed, which is to use a "has-a" relationship instead of a "is-a" relationship. It makes more sense logically and will make your class library more flexible. An animation or a sprite is not really a timer, it simply makes use of one to achive their intended behavior. The Animation class would contain a pointer to a Timer. The Sprite class would contain a pointer to a Timer and a Animation. These 3 classes does not need to inherit from each other at all.
Doh! I feel so silly.
Superb replies, thank you, AnonPoster, JungleJim and Odie. It just struck me so hard, I''m finding it hard to shake it off. It feels like I''ve been sleeping/dreaming all this time.
Overall it was more of an accademic question than practical question, but that line of problems bothered me for so long. I''ve always had bits here and there that just _didn''t_ fit in so called OO scheme... It''s so refreshing to be told confidently and straight to the point, namely:
I feel... so good and clear. lol. The thing was, I''ve come across a TRUCK LOAD of ideas and principles on How You Should Desgin Everything With OO Design. I''ve come across plenty of material, books, comments, talks, chats and ideas about Good Design can only be achieved by rigid OO structures. I''m not saying OOP is bad or useless - far from that. But, those messages are misleading, don''t you think?? I mean, the overall feel of all this OOP issue is nicely overdriven and hyper overrated in today''s society. Or something. I''ve always thought there are plenty of circumstances when OO Design just wasn''t appropriate. But I''ve been given an impression that I Should Somehow Find A Way and if I can''t, then I''m plain bad.
To give an example, one book I''ve come across (C++ game programming, or something like that, one of Trujillo''s books I think) was simply ALL about how to derive an entire GAME by pure inheritance and polymorphism. I mean, from the bones and skins. And that''s not the only one either, there have been plenty of "persuasive" material about How Everyone Should Do:
And I was like, ah, so if one can''t do it that way, then he is a bad programmer. But that''s not the case though, right?
Well, about the example situation above (about Timer class), it seems as if I''ve been complaining why cat wouldn''t bark and dog wouldn''t meow.
Thanks again!
Superb replies, thank you, AnonPoster, JungleJim and Odie. It just struck me so hard, I''m finding it hard to shake it off. It feels like I''ve been sleeping/dreaming all this time.
Overall it was more of an accademic question than practical question, but that line of problems bothered me for so long. I''ve always had bits here and there that just _didn''t_ fit in so called OO scheme... It''s so refreshing to be told confidently and straight to the point, namely:
quote:
"simple: don''t use inheritance, you haven''t made a case for needing it."
quote:
"There''s a distinction between the ''has-a'' relation and the ''is-a'' relation."
quote:
"a "pure inheritance based" design as you described it is not good OOP practice."
I feel... so good and clear. lol. The thing was, I''ve come across a TRUCK LOAD of ideas and principles on How You Should Desgin Everything With OO Design. I''ve come across plenty of material, books, comments, talks, chats and ideas about Good Design can only be achieved by rigid OO structures. I''m not saying OOP is bad or useless - far from that. But, those messages are misleading, don''t you think?? I mean, the overall feel of all this OOP issue is nicely overdriven and hyper overrated in today''s society. Or something. I''ve always thought there are plenty of circumstances when OO Design just wasn''t appropriate. But I''ve been given an impression that I Should Somehow Find A Way and if I can''t, then I''m plain bad.
To give an example, one book I''ve come across (C++ game programming, or something like that, one of Trujillo''s books I think) was simply ALL about how to derive an entire GAME by pure inheritance and polymorphism. I mean, from the bones and skins. And that''s not the only one either, there have been plenty of "persuasive" material about How Everyone Should Do:
|
And I was like, ah, so if one can''t do it that way, then he is a bad programmer. But that''s not the case though, right?
Well, about the example situation above (about Timer class), it seems as if I''ve been complaining why cat wouldn''t bark and dog wouldn''t meow.
Thanks again!
Really great question, really great answers. Good job, all.
I think the academic terms for "is-a" vs "has-a" are "composition" vs "inheritence", just FYI. Composition is a fancy way of saying that an object has other objects as members.
Also, I thought I'd throw out that once you get really, really into OOP, you should buy/read "Design Patterns" by Gamma, et. al. There are many, many, many more relationships between objects than "is-a" and "has-a"; this book describes a lot of them in great detail. Warning--it's very advanced.
Edited by - Stoffel on May 23, 2001 11:14:08 AM
I think the academic terms for "is-a" vs "has-a" are "composition" vs "inheritence", just FYI. Composition is a fancy way of saying that an object has other objects as members.
Also, I thought I'd throw out that once you get really, really into OOP, you should buy/read "Design Patterns" by Gamma, et. al. There are many, many, many more relationships between objects than "is-a" and "has-a"; this book describes a lot of them in great detail. Warning--it's very advanced.
Edited by - Stoffel on May 23, 2001 11:14:08 AM
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement
Recommended Tutorials
Advertisement