Why OOP?

Started by
81 comments, last by MaulingMonkey 18 years, 10 months ago
Quote:Original post by Anonymous Poster
Quote:Original post by Deyja
Wait - I just reliezed I need a w component! Uhuh! Maybe I was smart enough to use a defined constant for the size of my vectors...


Damn, probably shouldn't have named it Vector3f, huh? And what was that about changing to doubles?


Well, it's about here that routine kicks in and says that a Vector3f might very well be the best choice.
Advertisement
Thanks for the clarification. I was referring to procedural programming, not functional. Sorry for the misunderstanding.

Quote:Original post by Way Walker
Third, it makes it seem like inheritance is the most important aspect of OOP while most of the benefits of OOP come from encapsulation.


Yes, this is exactly my point. To me, encapsulation of data and methods has been the only real strength of OOP because VB6 only supports one form of polymorphism: implementation. (That's the "has-a" relationship, as opposed to the typical "is-a" that everyone else uses.) I suppose my problem is that I never analyzed OOP from a pure object-oriented standpoint such as a C# developer might have. My understanding of pure OOP is limited to UnrealScript, which is based on Java, and it's far from what you'd call a "pure" OO language.

So, just to clear things up, we're discussing OOP as a tool. That's all it is: a tool. You use a hammer to pound nails and a spoon to eat your soup. In the end, it's not about how you manage your objects as much as whether you use them at all. In my experience, I can replace a lot of classes with UDT's (structs to you). The end result looks the same to the user, except it's easier to write and manage.

Right. A Visual Basic user has no further business in this conversation. ;)

GDNet+. It's only $5 a month. You know you want it.

hi all, I have been following this thread along fairly nicely until around here. Ok, in this case of our vector class what is meant below when he says, "you did remember to use a typedef didn't you?" I am not sure what he is getting at.

I have an additional question about the bird analogy. In that case of the penguin, doesn't that just mean it should have been a different class hierarchy in stead? I think someone else said it as well, the assumption if immediately placing penguin under bird in the class hierarchy is the mistake. What about:

Bird
/ Flying birds Non-flying Birds
/ Cardinal Penguin

Isn't that just fine? Just leave the virtual fly() method out of the bird base class?

Also, this is covered in the "Principles of Object-Oriented Design" paper which is linked to over here near the bottom: http://www.gamedev.net/reference/list.asp?categoryid=66

I genuinely do wish to have some more information about using a typedef in this class declaration for vector that we're speaking of. I want to know because I think this touches on the same methods that can be used in other patterns such as creating an object factory.. if you wanted to make a vector using a generic object factory which is based off a template, where is the right place to use this typedef? we use all of these constructs as well in the commercial codebase that I work on which is not graphics, but financial software and in some ways I struggle to understand their use of these principles, mostly centered around dynamic memory allocation and creation for new objects. Sorry for the verbosity! Hope someone can help clarify!

Quote:Original post by Anonymous Poster
Quote:Original post by Deyja
Yes, it should. The two alternatives are 'Vector3 vec' or 'float vec[3]'. That should be enough to demonstrate which is preferable, but I will go further. With arrays, I have to remember how to declare it each time. I have to remember which index refers to which axis.


Remember? There's a fairly standard convention: (x, y, z). In nearly all cases, that's the order I've seen them in. If that convention is too weak for you, I'm surprised you find x, y, z any better. If +x is to the right, and +y is up, which way is +z? Is it pointing toward you (right-handed) or away from you (left-handed)? The former is commonly found in math, physics, and OpenGL. The latter is mostly in computer graphics. However, that's ignoring the fact that in 2D graphics, -y is usually up and in math and physics +z is usually up.

Quote:
What if I want to use doubles instead?


You did remember to use a typedef, didn't you?

Quote:
What about when I actually want to operate on the data? Do I accept arrays explicitly, or as raw pointers?


You do realize the following are equivalent:
void foo(float v[3]);
void foo(float v[]);
void foo(float *v);

Quote:
vec1 = Add(vec1,vec2)? Isn't vec1 += vec2 a lot easier to read?


I wouldn't necessarily say it's easier to read. Does
a = foo(a, b);
give you trouble?

Quote:
And how would ((v1*v3)+(v2*v3))/v4 look without a vector object?


Depends, you have two ambiguous operators. Is * the direct, dot, or cross product? Is / direct division or division by the magnitude? Seems
ScalarDivide(Add(Dot(v1, v3), Dot(v2, v3)), Magnitude(v4))
would be clearer.


Quote:Original post by jregan
I have an additional question about the bird analogy. In that case of the penguin, doesn't that just mean it should have been a different class hierarchy in stead?

That's precisely the point I was making though. OOP feels like a natural extension of real world concepts, but it isn't. In this particular example the hierarchy that naturally comes to mind and works for many cases sooner or later becomes inappropriate. So you try to modify it to bridge the gap. Aside from going through hell to modify the existing codebase, you'll then be presented with a plethora of design choices, each having a vital implication on the design. Do you do something like this:

Bird -> FlyingBird -> PigeonBird -> NonFlyingBird -> Penguin


or something like this:

Flyable -> Pigeon


Each has a lot of implications on your design that aren't immediately obvious to a beginner (this stuff is confusing for experts as well). It's easy to get started with, certainly far easier than functional concepts, but that's due to an illusion that OO design simply maps to natural world-view. When it comes to mastering OO, I think it becomes equally hard as mastering functional programming with a very questionable return.

EDIT: note that if you create a Bird class with a fly() abstract base it will like work for a long time. Once you discover that it doesn't work for a particular case you will have to go back and make appropriate changes. Even with refactoring it's a tremendous pain in the ass and is a huge time sink. As an OO expert in a traditional language (C#, C++, Java, etc.) you're required to predict roadblocks you might hit in the future. If you're using a dynamically dyped OO variant or a functional system you can simply make appropriate changes as they're required. This is clearly a better way to engineer large software because nobody no matter how good they are can predict the future.
Quote:Original post by jregan
hi all, I have been following this thread along fairly nicely until around here. Ok, in this case of our vector class what is meant below when he says, "you did remember to use a typedef didn't you?" I am not sure what he is getting at.


If you had done something like

class Vector3f { float x, y, z; /* ... */ };

or just littered your code with

float vec[3];

Then you're SOL if you need doubles instead of floats. However, if you'd done

typedef float Scalar;
class Vector3f { Scalar x, y, z; /* ... */ };

or

typedef float Vector3f[3];

then you just change the float to double in the typedef and all is well.

Quote:
I have an additional question about the bird analogy. In that case of the penguin, doesn't that just mean it should have been a different class hierarchy in stead? I think someone else said it as well, the assumption if immediately placing penguin under bird in the class hierarchy is the mistake. What about:

Bird
/ Flying birds Non-flying Birds
/ Cardinal Penguin

Isn't that just fine? Just leave the virtual fly() method out of the bird base class?


You've missed the point. Yeah, if I see I'll someday need to consider flightless birds, then I'd just make the hierarchy you mentioned. But what if to start with I'm considering pigeons, robins, and cardinals? I'll be like "So, all the birds need a fly() method, I'll just put that in the base class". A year down the line I (or my replacement if I've moved onto bigger and better things) am told that we're now considering penguins, as well. What was a good design a year ago just turned sour.

In some cases, it's probably best to just make fly() a no-op on penguins. In others, it may be better to make it do the equivalent penguin thing. In others, restructuring the hierarchy is the best bet. But in all cases, the design that made sense at the beginning is causing trouble now. In more complicated cases, it'd be even harder to see a problem like this coming.

Quote:
I genuinely do wish to have some more information about using a typedef in this class declaration for vector that we're speaking of. I want to know because I think this touches on the same methods that can be used in other patterns such as creating an object factory.. if you wanted to make a vector using a generic object factory which is based off a template, where is the right place to use this typedef? we use all of these constructs as well in the commercial codebase that I work on which is not graphics, but financial software and in some ways I struggle to understand their use of these principles, mostly centered around dynamic memory allocation and creation for new objects. Sorry for the verbosity! Hope someone can help clarify!


I think what you need are straight up templates. Try google for a good tutorial. typedefs are a less powerful tool for simpler problems (like deciding at compile time whether to use floats, doubles, or long doubles).
Quote:Original post by Anonymous Poster
But what if to start with I'm considering pigeons, robins, and cardinals? I'll be like "So, all the birds need a fly() method, I'll just put that in the base class". A year down the line I (or my replacement if I've moved onto bigger and better things) am told that we're now considering penguins, as well. What was a good design a year ago just turned sour.

To be fair, this is only a problem with statically typed languages. In a dynamically typed language moving things to base classes just to satisfy the type system isn't necessary.
Quote:Original post by CoffeeMug
To be fair, this is only a problem with statically typed languages. In a dynamically typed language moving things to base classes just to satisfy the type system isn't necessary.


I wouldn't go so far as to say that this isn't a problem in dynamic languages. Say we have a Bird class with fly and layEgg methods. We have Pigeons, Cardinals, and Eagles which inherit from this class. Eagle overrides fly so that it can soar, but the other two just inherit the generic behavior. Now, it comes about that you want to add Penguin. You still want to inherit layEgg, but what about fly? Do you:
- Make it a no-op?
- Throw and exception?
- Record a diagnostic?
- Override it with swimming behavior?
- Reorganize the tree?
- Say "Not my problem"?
All have pros and cons. The question is which pros and which cons are important to the current situation?

However, many of these problems do disappear in dynamic languages. Often times you don't even need a base class except to possibly add some documentation. Very little difference between "is a" and "has a".
I didnt ever think OOP was all it is cracked up to be. But, i designed a windows-style GUI, and in it used OOP. I now realise that i didnt fully appreciate OOP because i didnt fully understand it. I now write all my code OOP and the benefits are beyond measure.
SynexCode Monkey
That fly() is a problem from theoretical viewpoint because when we inherit we are saying that the child object will exhibit the same behavior as it's parent and do more as well. But here we're breaking the flying behavior by not flying. The other problem is that now you have an extra penguin object to iterate over in a list for example. I think it makes sense to separate birds into flying and non-flying ones and then to iterate over flying ones invoking fly() method on them. I find that when I used to program in C that I didn't think much about the design and paid a price in the end. So now I use C++ instead of mimicking it in C which I would end up doing if I stayed with C. Probably going with modular programming style. Thus OOP seems harder because now we're faced with lot more questions since we're venturing into design realm. Those same questions would needed to be asked in C eventually so we actually get a step ahead in C++ by asking about them early on and avoiding some headaches later on.
'Fly' is a poor level of abstraction. It could be called 'Move' and flying birds can implement that with their Fly function, or a Bird could return a Controller object which can call the correct function. Going with that idea an Ostrich might have a Run function, a Penguin a Swim function and an African Swallow a Fly function. The Controller would be derived for each Bird and when asked to Move the Bird it would call Run, Fly or Swim appropriately.

There's no need for redundant do nothing functions, or throwing exceptions. In fact there is no need to derive from Bird, rather something Controllable which returns a Controller.

[Edited by - petewood on June 8, 2005 8:51:55 AM]

This topic is closed to new replies.

Advertisement