|
|
|
#1 |
|
Messages: n/a
Hébergeur: |
Hi,
While I think I have a reasonable grasp of C++ in general, I've never really used it more than a convenient C. as such, I'm finding very basic OO confusing, not so much the concept, but how everything should be laid out in practice. One of those is deciding whether or not a class should be inherited or declared as an object in the class. note: this isn't isn't a specific problem, just making pseudo code as an example. class RootProgramClass : public ConfigClass, public Window { } vs. class RootProgramClass { ConfigClass config; Window window; } How about appropriate use of constructors? something tells me a constructor taking arguments just to pass onto an inherited classes constructor, to pass onto the final classes constructor, to initialize the classes variables, isn't very good design. Are there ways to avoid this mess and allow the programmer to initialize an double inherited classes variables neatly. these are all newbish questions and it shames me to ask them, unfortunately I'm nothing more than a hobbyist so don't have the benefit of university level courses, etc. any resources on good OO design, etc. would be appreciated. |
|
|
|
#2 |
|
Messages: n/a
Hébergeur: |
superheathen@yahoo.ca wrote:
> While I think I have a reasonable grasp of C++ in general, I've never > really used it more than a convenient C. as such, I'm finding very > basic OO confusing, not so much the concept, but how everything should > be laid out in practice. > > One of those is deciding whether or not a class should be inherited > or declared as an object in the class. > > note: this isn't isn't a specific problem, just making pseudo code as > an example. > > class RootProgramClass : public ConfigClass, public Window > { > } > > vs. > > class RootProgramClass > { > ConfigClass config; > Window window; > } The only reason for public inheritance is to extend the class from which you inherit. For example, you have a generic Window that does not really do much, only knows how to report its own position, say, and has a bunch of virtual functions to respond to some system "messages", then any window you create will probably be descending from that type... So, according to very early teachings on OOD, the relationship between your CustomWindow and a generic Windows exists and it's the "is-a" relationship. CustomWindow "is-a" Window. Or, you have some kind of extended configuiration class that doesn't just keep your configuration (and overrides some generic ConfigClass behaviour), but also provides additional elements, like storing the configuration settings somewhere special (like Internet). Then you basically extend your ConfigClass, and the CustomConfigurator "is-a" ConfigClass. Now, speaking of your concrete example... Considering the names of the classes, I'd probably go for the latter. What is the purpose/functionality of 'RootProgramClass'? While you didn't specify it, if I just suppose that it's like a singleton that keeps some important things while your program is running, if it's like what is often called "the Application object", then the program "has-a" config and "has-a" window, but it isn't any of those things. As you can see "is-a" and "has-a" are two fundamental types of entity relationships in a design. NewsgroupReader "is-a" InternetUser, and FtpUploaderDownloader "is-a" InternetUser. They share some common traits and functionality, probably. ElectricCar "has-a" motor, and at the same time ElectricCar "is-a" vehicle... > How about appropriate use of constructors? Huh? > something tells me a > constructor taking arguments just to pass onto an inherited classes > constructor, to pass onto the final classes constructor, to initialize > the classes variables, isn't very good design. Are there ways to avoid > this mess and allow the programmer to initialize an double inherited > classes variables neatly. Methinks you're approaching it from the wrong side. Don't design the software or class hierarchy from the POV of using constructors. Design from the POV of entity relationships. Design from the POV of interfaces. Essentially, you need to say, "who and how is going to use my class[es]?" Then you will have the initial layout of your model represntation. Then you take a look at the implementation of that first layout, and probably figure out the other pieces of the framework (every solution has some kind of framework, even if it doesn't name it that). Then you just keep going through the design iterations until you have it finalized. By that time you will probably have it half-implemented. > these are all newbish questions and it shames me to ask them, > unfortunately I'm nothing more than a hobbyist so don't have the > benefit of university level courses, etc. > > any resources on good OO design, etc. would be appreciated. Try 'comp.object', OOD is their very topic. V -- Please remove capital 'A's when replying by e-mail I do not respond to top-posted replies, please don't ask |
|
|
|
#3 |
|
Messages: n/a
Hébergeur: |
Victor Bazarov wrote:
> The only reason for public inheritance is to extend the class from > which you inherit. For example, you have a generic Window that does > not really do much, only knows how to report its own position, say, > and has a bunch of virtual functions to respond to some system > "messages", then any window you create will probably be descending > from that type... So, according to very early teachings on OOD, > the relationship between your CustomWindow and a generic Windows > exists and it's the "is-a" relationship. CustomWindow "is-a" Window. Another basic concept of object-oriented design (which can have exceptions, but is still a good rule-of-thumb) is that a base class is a more abstract concept than a derived class, which is a more concrete concept. The classic example is a class hierarchy related to living organisms. For example, you can have a class named "Animal" and a class derived from it called "Dog". "Animal" is a more abstract concept: It defines the properties which are common to all animals. "Dog" is a more concrete concept: It defines all the same properties as "Animal", but besides them it defines additional properties which are exclusive to dogs. It's thus a more concrete definition at a conceptual level. You could also have a class named "Cat", which is derived from "Animal". It defines a different type of animal, with its own properties. This nicely follows the "is-a" relationship: A "Dog" is an "Animal", a "Cat" is an "Animal", but a "Dog" is not a "Cat". They are distinct classes. A function taking an "Animal" can be given a "Dog" or a "Cat", but a function taking a "Dog" cannot be given a "Cat". This also answer the classic OOD problem: If we have circles and ellipses, should circle be inherited from ellipse, or the other way around. Maybe circle should be inherited from an ellipse? After all, a circle *is* an ellipse, just with a constraint. It's a special type of ellipse. However, if we do that, we encounter problems: For example, an ellipse has two radiuses, but a circle has only one. Thus a circle does *not* implement all the properties of an ellipse. So maybe the ellipse should be inherited from a circle? After all, an ellipse can be thought as an extension of a circle. However, this also causes problems: If a function taking a circle asks it for its radius, what should the ellipse return? The major radius? The minor one? Some combination? Not good. The answer is that neither should be inherited from the other. This is because a circle and an ellipse are at the *same* conceptual level. They are both drawing primitives. You can't really say that one concept is more abstract than the other. They are both equally concrete concepts. Thus inheriting one from the other would be wrong. The correct solution is to create a more abstract concept which is common to both circles and ellipses (and other similar drawing primitives), for example a "DrawingPrimitive" class, and inherit "Circle" and "Ellipse" from it. Thus a circle *is a* drawing primitive, and an ellipse *is a* drawing primitive. |
|
|
|
#4 |
|
Messages: n/a
Hébergeur: |
* Juha Nieminen:
> [About circles and ellipses] > The answer is that neither should be inherited from the other. This is > because a circle and an ellipse are at the *same* conceptual level. Generally, when that's the case, the person's concepts are flawed :-). E.g., a constant circle Is-A constant ellipse (the general relationship is a bit more complicated). In C++ this can be expressed via interfaces or via implicit conversion. [snip] > The correct solution is to create a more abstract concept which is > common to both circles and ellipses (and other similar drawing > primitives), for example a "DrawingPrimitive" class, and inherit > "Circle" and "Ellipse" from it. Sorry, but there's no general "the" correct solution. Cheers, & hth., - Alf -- A: Because it messes up the order in which people normally read text. Q: Why is it such a bad thing? A: Top-posting. Q: What is the most annoying thing on usenet and in e-mail? |
|
|
|
#5 |
|
Messages: n/a
Hébergeur: |
On Dec 7, 7:17 pm, Juha Nieminen <nos...@thanks.invalid> wrote:
> Victor Bazarov wrote: > > The only reason for public inheritance is to extend the class from > > which you inherit. For example, you have a generic Window that does > > not really do much, only knows how to report its own position, say, > > and has a bunch of virtual functions to respond to some system > > "messages", then any window you create will probably be descending > > from that type... So, according to very early teachings on OOD, > > the relationship between your CustomWindow and a generic Windows > > exists and it's the "is-a" relationship. CustomWindow "is-a" Window. > > Another basic concept of object-oriented design (which can have > exceptions, but is still a good rule-of-thumb) is that a base class is a > more abstract concept than a derived class, which is a more concrete > concept. > > The classic example is a class hierarchy related to living organisms. > For example, you can have a class named "Animal" and a class derived > from it called "Dog". > "Animal" is a more abstract concept: It defines the properties which > are common to all animals. "Dog" is a more concrete concept: It defines > all the same properties as "Animal", but besides them it defines > additional properties which are exclusive to dogs. It's thus a more > concrete definition at a conceptual level. > You could also have a class named "Cat", which is derived from > "Animal". It defines a different type of animal, with its own properties. > > This nicely follows the "is-a" relationship: A "Dog" is an "Animal", a > "Cat" is an "Animal", but a "Dog" is not a "Cat". They are distinct > classes. A function taking an "Animal" can be given a "Dog" or a "Cat", > but a function taking a "Dog" cannot be given a "Cat". > > This also answer the classic OOD problem: If we have circles and > ellipses, should circle be inherited from ellipse, or the other way around. > Maybe circle should be inherited from an ellipse? After all, a circle > *is* an ellipse, just with a constraint. It's a special type of ellipse. > However, if we do that, we encounter problems: For example, an ellipse > has two radiuses, but a circle has only one. Thus a circle does *not* > implement all the properties of an ellipse. > So maybe the ellipse should be inherited from a circle? After all, an > ellipse can be thought as an extension of a circle. However, this also > causes problems: If a function taking a circle asks it for its radius, > what should the ellipse return? The major radius? The minor one? Some > combination? Not good. > > The answer is that neither should be inherited from the other. This is > because a circle and an ellipse are at the *same* conceptual level. They > are both drawing primitives. You can't really say that one concept is > more abstract than the other. They are both equally concrete concepts. > Thus inheriting one from the other would be wrong. > > The correct solution is to create a more abstract concept which is > common to both circles and ellipses (and other similar drawing > primitives), for example a "DrawingPrimitive" class, and inherit > "Circle" and "Ellipse" from it. Thus a circle *is a* drawing primitive, > and an ellipse *is a* drawing primitive. Yeah, the is-a has-a relationships seem blatantly obvious in some examples, such as Dog : Animal Civic : Car, a car isn't an engine, but it has one, so Car should contain engine rather than inherit from it. Sometimes it just doesn't seem so clear to me though. I'm starting to think though that my problem isn't lack of understanding in regards to composition vs. inheritance, which seems pretty straightforward, but the areas in which I'm programming. For a better example than the one I originally provided consider this: You have a Game class, an Engine class, and a Window class. The Window class is responsible for say, resizing, OpenGL initialization, and updating the window. The Engine class is responsible for monitoring input and dispatches events to virtual functions which are overridden in the Game class and tied to game logic, like updating position of objects, etc. The only way I can think of making this work is Game inheriting Engine inheriting Window. We come back to is-a and has-a. Should Engine have a Window? or should Engine be an extension of Window? Should a Game class have an Engine? or should a Game class been an extension of an Engine? extension of _x is the only way I could conceive of it working, but it doesn't quite seem right. Perhaps I should be asking these questions on a game development group or forum. ;-) |
|
|
|
#6 |
|
Messages: n/a
Hébergeur: |
superheathen@yahoo.ca wrote:
> [..] I'm starting to think though that my > problem isn't lack of understanding in regards to composition vs. > inheritance, which seems pretty straightforward, but the areas in > which I'm programming. It seems that once the relationship has been established, you can tell whether it's logical or not, but when you have a set of your own types/concepts, figuring out the relationship between them is a challenge. > For a better example than the one I originally provided consider this: > You have a Game class, an Engine class, and a Window class. Is "Model/View/Controller" pattern something you'd be intersted in? The pattern is very common and many books have been written around it. > The Window class is responsible for say, resizing, OpenGL > initialization, and updating the window. The Engine class is > responsible for monitoring input and dispatches events to virtual > functions which are overridden in the Game class and tied to game > logic, like updating position of objects, etc. The only way I can > think of making this work is Game inheriting Engine inheriting > Window. We come back to is-a and has-a. > > Should Engine have a Window? or should Engine be an extension of > Window? Should a Game class have an Engine? or should a Game class > been an extension of an Engine? Start by reading/thinking about MVC layout of your program. What is the view? A window. What does it do? What is the role of the view in that architecture? What is playing the controller? What is the model? The main question you need to ask (and answer) is "what does it do", about each of the pieces in your application. Try not to start with "I have an Engine, a Game, and a Window -- who owns what and who is what?" Those are the wrong questions to ask. Why do you have an Engine? Well, you probably need one (or more) to make your application do something, but is it (or are they) really the top-level element(s)? Design from bottom up is difficult if not wrong. > extension of _x is the only way I could conceive of it working, but it > doesn't quite seem right. Perhaps I should be asking these questions > on a game development group or forum. ;-) The more venues you visit, the more information you'll collect, and that's A GOOD THING(tm). Of course, developing a game is not really different from developing a CAD system or a Banking system. All have data, all have UI. All have components you get from 3rd parties (engines, libraries). But don't start with "I have a brick and a board, what house do I build from those?" Start by asking what house you want, what should it have, and then arrive to bricks and boards you may need to accomplish your goals. V -- Please remove capital 'A's when replying by e-mail I do not respond to top-posted replies, please don't ask |
|
|
|
#7 |
|
Messages: n/a
Hébergeur: |
In article
<ee2c5418-dfbe-4286-8092-d56cec1b99c5@s12g2000prg.googlegroups.com>, superheathen@yahoo.ca wrote: > Hi, > > While I think I have a reasonable grasp of C++ in general, I've never > really used it more than a convenient C. as such, I'm finding very > basic OO confusing, not so much the concept, but how everything should > be laid out in practice. > > One of those is deciding whether or not a class should be inherited > or declared as an object in the class. I find that the Liskov Substitution Principle (GIYF) is a useful and concrete way to determine if public inheritance is appropriate. Stated simply, the LSP requires that a publicly derived class be substitutible for any instance of the base class. In other words, if class D publicly derives from class B, then everywhere you have a B* pointing to a B object, you can substitute a D object with no change to the algorithm semantics. In practical terms, this may mean that if you have a unit test for B, you must be able to pass that same unit test with D. Here are some other guidelines: For "is-a" relationships, use public inheritance. For "has-a" relationships, use composition (member variables). For "is-implemented-in-terms-of" relationships, use private inheritance. Never, never, ever use protected inheritance. -dr |
|
|
|
#8 |
|
Messages: n/a
Hébergeur: |
Alf P. Steinbach wrote:
> * Juha Nieminen: >> [About circles and ellipses] The answer is that neither should be >> inherited from the other. This is >> because a circle and an ellipse are at the *same* conceptual level. > > Generally, when that's the case, the person's concepts are flawed :-). > > E.g., a constant circle Is-A constant ellipse (the general relationship > is a bit more complicated). IMO that's the wrong usage of the "is-a" relationship. IMO "is-a" is more related to abstract/concrete than to human language. Even though you can *say* "thing A is a thing B", that doesn't mean that B is a more abstract concept than A. IMO the "is-a" relation should always be applied with levels of abstraction. An ellipse is not a more abstract concept than a circle. They are both equally concrete concepts. (And in fact, in a way, one could even argue that an ellipse is slightly more concrete than a circle because the ellipse has more information in it than a circle.) I would say that from OO point of view a circle is *not* an ellipse, even if in mathematics you could say that it is. However, OOD is a different field from mathematics. > In C++ this can be expressed via interfaces or via implicit conversion. Even if you can, that doesn't mean you should. >> The correct solution is to create a more abstract concept which is >> common to both circles and ellipses (and other similar drawing >> primitives), for example a "DrawingPrimitive" class, and inherit >> "Circle" and "Ellipse" from it. > > Sorry, but there's no general "the" correct solution. You are welcome to suggest a better solution from OOD point of view. |
|
|
|
#9 |
|
Messages: n/a
Hébergeur: |
superheathen@yahoo.ca wrote:
> The Window class is responsible for say, resizing, OpenGL > initialization, and updating the window. The Engine class is > responsible for monitoring input and dispatches events to virtual > functions which are overridden in the Game class and tied to game > logic, like updating position of objects, etc. The only way I can > think of making this work is Game inheriting Engine inheriting > Window. We come back to is-a and has-a. Actually that sounds to me like a clear case where you should use a callback mechanism. Your game engine class is not a window, it's an event listener. Thus it should be inherited from an event listener interface/class. When you instantiate your Window class, you give a pointer/reference to your game engine (which "is-a" event listener). The Window instance just sees a pointer/reference of type event listener and calls its virtual functions when events happen. (There are advantages in doing it like this. For example, you can change the event listener which the Window instance dispatches the events to on the fly. You could even have more than one event listener which receive events from the same Window instance.) |
|
|
|
#10 |
|
Messages: n/a
Hébergeur: |
* Juha Nieminen:
> Alf P. Steinbach wrote: >> * Juha Nieminen: >>> [About circles and ellipses] The answer is that neither should be >>> inherited from the other. This is >>> because a circle and an ellipse are at the *same* conceptual level. >> Generally, when that's the case, the person's concepts are flawed :-). >> >> E.g., a constant circle Is-A constant ellipse (the general relationship >> is a bit more complicated). > > IMO that's the wrong usage of the "is-a" relationship. IMO "is-a" is > more related to abstract/concrete than to human language. Even though > you can *say* "thing A is a thing B", that doesn't mean that B is a more > abstract concept than A. IMO the "is-a" relation should always be > applied with levels of abstraction. > > An ellipse is not a more abstract concept than a circle. They are both > equally concrete concepts. (And in fact, in a way, one could even argue > that an ellipse is slightly more concrete than a circle because the > ellipse has more information in it than a circle.) > > I would say that from OO point of view a circle is *not* an ellipse, > even if in mathematics you could say that it is. However, OOD is a > different field from mathematics. I think you didn't read what you replied to. Anyway, I would just have to write the same again. If you don't understand, then you don't, but you could try (one good way is to actually "do", i.e. designing and implementing this). >> In C++ this can be expressed via interfaces or via implicit conversion. > > Even if you can, that doesn't mean you should. > >>> The correct solution is to create a more abstract concept which is >>> common to both circles and ellipses (and other similar drawing >>> primitives), for example a "DrawingPrimitive" class, and inherit >>> "Circle" and "Ellipse" from it. >> Sorry, but there's no general "the" correct solution. > > You are welcome to suggest a better solution from OOD point of view. I have. Cheers, & hth., - Alf -- A: Because it messes up the order in which people normally read text. Q: Why is it such a bad thing? A: Top-posting. Q: What is the most annoying thing on usenet and in e-mail? |
|
![]() |
| Outils de la discussion | |
|
|