Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411423 Posts in 69363 Topics- by 58416 Members - Latest Member: JamesAGreen

April 19, 2024, 06:35:28 AM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsDeveloperTechnical (Moderator: ThemsAllTook)OO-itis
Pages: [1] 2
Print
Author Topic: OO-itis  (Read 2565 times)
Vadinci
Level 0
**



View Profile
« on: September 16, 2014, 06:02:35 AM »

Food for thought for those still infected with OO-itis; hard to help myself some times Wink

I think I'm cured of oo-itis.

be careful. it hibernates.

I see 'OO-itis' popping up every now and then on TIGsource, as well as pleads to be cautious of OO-ing too much in general. As someone who is now pretty confident in my (game) programming (I'll make things work one way or the other, eventually), and currently struggling to improve my code quality (more modularity, less redundancy, low coupling, etc.):

What are tell-tale signs of OO-itis?
What are common problems that would be better solved with something non-OOP?
What are good alternative programming styles?

Basically OOP is the only thing I really know, and I'm looking for tips on how to expand my programming knowledge Durr...?
Logged
Sik
Level 10
*****


View Profile WWW
« Reply #1 on: September 16, 2014, 06:13:24 AM »

Rule of thumb: if it's not something you can imagine as some sort of entity (tangible or not), it probably shouldn't be OOP. Which also probably means you're looking at making new functions and not new classes (though to be fair, this really depends on a case-by-case basis).
Logged
jgrams
Level 3
***



View Profile
« Reply #2 on: September 16, 2014, 07:24:05 AM »

Disclaimer: I come from the other end of the spectrum (assembly language, C, etc.)...

Since speed can be an issue with games, there are some concerns that OO gives poor memory layout for modern systems where memory latency is high relative to CPU speed. Search for "Data Oriented Design" for info on that stuff. I think Noel Llopis's article is fairly good introduction to the idea. He goes a little overboard the other way, maybe, but it's good food for thought.

Beyond that...I have the impression that people who are really good with object-oriented design can use it to good effect just about anywhere, and that it's more inappropriate use or misunderstanding of OO principles that's the problem, e.g. excessive inheritance rather than composition, layers of factories or other abstractions, and so on. But I don't really do enough OO to be able to come up with good examples. I do know that from a maintenance perspective I've seen other people's code which is really hard to understand because you have to chase through four or five levels of classes to find the place where the actual work is done, and it's hard to tell which behavior would get executed at runtime...
Logged
nox
Level 0
***



View Profile WWW
« Reply #3 on: September 16, 2014, 10:18:12 AM »

Quote
What are common problems that would be better solved with something non-OOP?

Most of them. Depending on the language we're talking about, the only motivation for OOP is convenient runtime polymorphism -- anything else can be achieved by other means. The cost of OOP is not small. Firstly, from a cognitive perspective, it results in designs whose structure and function can not be understood at a glance. The performance implications are a lesser concern, but worth knowing for the few situations in which OOP will bite you.

I wrote an article about my thoughts on OOP a while ago, it's got some useful links.

Quote
What are good alternative programming styles?

A good start is  to realize that the OO syntax:

Code:
object->verb()

is the same as:

Code:
verb(object)

and write code that way instead whenever possible. It's a more honest notation, because it discourages hidden state. Beyond that you should check out functional programming for a general purpose paradigm generally devoid of OOP. Learning how to solve problems functionally is eye opening and will help you write "better" code.
Logged

Boreal
Level 6
*


Reinventing the wheel


View Profile
« Reply #4 on: September 17, 2014, 04:09:15 PM »

OOP is the practice of making code easier for people to understand at the risk of making it harder for the computer to understand.
Logged

"In software, the only numbers of significance are 0, 1, and N." - Josh Barczak

magma - Reconstructed Mantle API
anthnich
Level 1
*



View Profile WWW
« Reply #5 on: September 17, 2014, 04:49:21 PM »

Another one is making a ton of unneeded child classes. If a class doesn't need the majority of the functionality of the parent and doesn't leverage polymorphism, than it probably shouldn't be a child class.
Logged

Turnover @ Steam
InfiniteStateMachine
Level 10
*****



View Profile
« Reply #6 on: September 18, 2014, 10:31:54 AM »

In my particular case where you quoted me I was referring to too much use of inheritance and polymorphism combined with using one monster list of baseclass pointers to do the update/render.

It's not much more work to make a separate collection for each type of object I'm handling and it makes things like sorting and general management later easier to do.

These days I rarely go further than inheriting from an interface.
Logged

Sik
Level 10
*****


View Profile WWW
« Reply #7 on: September 18, 2014, 02:07:21 PM »

One thing that irks me from OOP that's a non-issue for procedural programming. Let's say that for example I have this type that specifies a version number (replace struct with class for OOP, you get the idea):

Code:
struct Version {
   unsigned major;
   unsigned minor;
   unsigned patch;
}

Now let's say i want to write code that determines if one version is compatible against another (backwards compatibility, etc.). With procedural programming, it's easy, just write a function (ignore the correctness of the types, just trying to get the point across):

Code:
bool are_compatible_versions(Version which, Version against) {
   ...
}

But then the problem is with OOP. How would you make this "OOP-clean"? You could make the function a member and then have one of the versions as the object on which the member function gets called (e.g. which.is_compatible(against)), but in hindsight that doesn't make much sense, does it? You can't overload operators because the function doesn't fit any of the preexisting ones (so trying that would be bad design!). You could just make it a separate (i.e. non-member) function, but then it isn't OOP-clean, is it?

Just illustrating a pretty obvious issue I'm seeing with OOP that I've been thinking on lately. The whole member function thing works fine when it works on one object only. When two objects are involved, the "ideal" model falls apart and you need to break it somehow.
Logged
Paul Jeffries
Level 3
***



View Profile WWW
« Reply #8 on: September 18, 2014, 04:09:47 PM »

But then the problem is with OOP. How would you make this "OOP-clean"? You could make the function a member and then have one of the versions as the object on which the member function gets called (e.g. which.is_compatible(against)), but in hindsight that doesn't make much sense, does it?

I'm not entirely sure why you say this doesn't make sense, but to my mind it only doesn't make sense because that function doesn't belong in the version struct at all.  I would instead include it on whatever class is using that struct to store its version number, because compatibility between versions will be a quality of that class, not the version number itself.  You might have several different classes using the same Version struct, some of which will be backwards compatible and some of which might not, for example.  Putting that function on the version number itself would be doing OOP 'wrong', because it would require that struct to have knowledge of the implementation details of another class.
Logged

www.vitruality.com | SPARTAN - Small Pixel Art Animator and procedural tile generator
Boreal
Level 6
*


Reinventing the wheel


View Profile
« Reply #9 on: September 18, 2014, 05:15:53 PM »

But then the problem is with OOP. How would you make this "OOP-clean"? You could make the function a member and then have one of the versions as the object on which the member function gets called (e.g. which.is_compatible(against)), but in hindsight that doesn't make much sense, does it? You can't overload operators because the function doesn't fit any of the preexisting ones (so trying that would be bad design!). You could just make it a separate (i.e. non-member) function, but then it isn't OOP-clean, is it?

The conventional OOP solution would be to overload the "==" operator.  I'm really not a fan of operator overloading if I don't 100% trust the codebase to not make ">" open a file or something.
Logged

"In software, the only numbers of significance are 0, 1, and N." - Josh Barczak

magma - Reconstructed Mantle API
Sik
Level 10
*****


View Profile WWW
« Reply #10 on: September 18, 2014, 07:33:53 PM »

I would instead include it on whatever class is using that struct to store its version number, because compatibility between versions will be a quality of that class, not the version number itself.

Wrong, and even if you ignore that, how would you apply this logic to some other type of object where it's indeed 100% sure to be a property of that object?

The conventional OOP solution would be to overload the "==" operator.  I'm really not a fan of operator overloading if I don't 100% trust the codebase to not make ">" open a file or something.

The problem is that I'd expect == to say that two versions are identical (i.e. all major, minor and patch are the same in both versions), you can have compatibility without that being the case (e.g. 1.4.3 would work with 1.5.2 even though they definitely are not the same version).
Logged
Paul Jeffries
Level 3
***



View Profile WWW
« Reply #11 on: September 19, 2014, 01:39:55 AM »

Wrong, and even if you ignore that, how would you apply this logic to some other type of object where it's indeed 100% sure to be a property of that object?

Other versioning schemes are available, but OK; if you are using that convention and everybody involved in the project who will ever be in a position to give a version number to something knows that then you could attach the function to the version struct.  But in that case I don't see why you think doing that wouldn't make sense.  If something is indeed a property of that object then what exactly would be the problem with making it a member function?
Logged

www.vitruality.com | SPARTAN - Small Pixel Art Animator and procedural tile generator
riksteri
Level 0
**



View Profile
« Reply #12 on: September 19, 2014, 07:00:15 AM »

You could just make it a separate (i.e. non-member) function, but then it isn't OOP-clean, is it?

I think it's besides the point you're making, but I don't see why mixing and matching like this would be wrong. If one does have a perfectly good solution, seems silly to not use it only because it doesn't happen to go with a specific paradigm. I mean, the whole issue you're describing seems to go away if one gets rid of the notion that "OOP-clean" is some kind of end goal in itself.

On the other hand, some languages don't even have non-member functions, and one would be restricted to making some goofily named utility class with a static method, or the like.
Logged
Scott
Level 2
**


View Profile WWW
« Reply #13 on: September 19, 2014, 07:46:40 AM »

OOP is the practice of making code easier for people to understand at the risk of making it harder for the computer to understand.
Beautiful and simple.

There appears to be 2 topics regarding OOP in this thread:

Data organization
OOP makes it easy for us to create and manage large code bases and understand how the various systems work together. When we debug it is far easier to see the state of a certain object versus Data-oriented-design. However, computers are made for processing data and OOP tends to organize data in a totally different manner than the computer would ideally have it memory. This is why you get huge bottlenecks in areas such as cache misses etc. Also to consider, OOP and DOD are not mutually exclusive!

Functionality organization
As previously mentioned, mixing and matching member and non-member functions is certainly fine. I work for a large Japanese camera manufacturer in the micro-biological research industry, and developed the in-house imaging library. We do have classes for objects such as Image, Blob, Font, etc., and they have functions specifically for managing the objects themselves, such as allocation, direct memory access, etc. There is also a huge library of standalone functions which uses these objects as inputs and outputs. These are generally algorithms of one sort or another which act on the data whereas the class member functions manage the data.

It would be awkward to organize the functions in a different way, honestly. We would have done it similarly whether in C, C++, C#, or Java.

As far as inheritance goes (which influences both functionality and data organization), this:

In my particular case where you quoted me I was referring to too much use of inheritance and polymorphism combined with using one monster list of baseclass pointers to do the update/render.

It's not much more work to make a separate collection for each type of object I'm handling and it makes things like sorting and general management later easier to do.

These days I rarely go further than inheriting from an interface.
« Last Edit: September 19, 2014, 07:52:22 AM by Scott » Logged

RandyGaul
Level 1
*

~~~


View Profile WWW
« Reply #14 on: September 19, 2014, 08:35:10 AM »

OOP is the practice of making code easier for people to understand at the risk of making it harder for the computer to understand.
Yeah! This. OOP is a methodology that a lot of programmers try to follow in hopes of achieving code that is easier for people to understand. I'm not sure what others on this forum mean when they say OO-itis, but I'd imagine it's someone who has terrible habits from an earlier time, or someone that doesn't understand hardware/memory very well.
Logged
InfiniteStateMachine
Level 10
*****



View Profile
« Reply #15 on: September 19, 2014, 08:40:31 AM »

OOP is the practice of making code easier for people to understand at the risk of making it harder for the computer to understand.
Beautiful and simple.

There appears to be 2 topics regarding OOP in this thread:

Data organization
OOP makes it easy for us to create and manage large code bases and understand how the various systems work together. When we debug it is far easier to see the state of a certain object versus Data-oriented-design. However, computers are made for processing data and OOP tends to organize data in a totally different manner than the computer would ideally have it memory. This is why you get huge bottlenecks in areas such as cache misses etc. Also to consider, OOP and DOD are not mutually exclusive!


Yeah this is something to definitely be aware of. A while ago there was a guy on here (no longer posts here anymore) saying that component oriented design is much better than oop for cache coherency but all his examples had to do with data layout. There's nothing stopping you from having the separate parts of an object exist in a contiguous backing store. Whether or not that's good design is debatable but the point is that data layout isn't directly a result of OOP or component oriented design. The only real issue would be something like a vtable jump.

OOP is the practice of making code easier for people to understand at the risk of making it harder for the computer to understand.
Yeah! This. OOP is a methodology that a lot of programmers try to follow in hopes of achieving code that is easier for people to understand. I'm not sure what others on this forum mean when they say OO-itis, but I'd imagine it's someone who has terrible habits from an earlier time, or someone that doesn't understand hardware/memory very well.

Funnily enough, OOP-heavy java/c# with deep inheritance is usually the hardest code to read and manipulate (for me at least).

As for what you mean by oop-itis, it's been described pretty well in this thread already.
Logged

Photon
Level 4
****


View Profile
« Reply #16 on: September 19, 2014, 05:59:36 PM »

I was more or less taught OOP back in college but I've had adequate experience in languages from C++ to Assembler languages.

Its not really something I'd given much thought to before, but if I'm interpreting what I see here correctly, the "bad kind of OOP" (or at least one form of it) is something like this:

Code:
score.increment(x)
timer.increment(x)
health.increment(x)

Increment essentially is the same thing for all these objects, so why have three separate calls? The thrust of what I'm getting here is that having increment as one separate call is much better and then the objects just have some sort of naming/variable convention that makes them compatible with the function. For a much larger subset of objects, the simplicity and performance gained is of a higher factor.

That's a very basic example, of course. There are certainly times where I think the object would want more control over how some of its actions are done.
Logged
pfaffian
Level 0
*


View Profile
« Reply #17 on: September 20, 2014, 01:06:27 AM »

OOP is just introduced way too early to new programmers.
A lot of introductory books on programming it's the first thing they introduce after the usual 'Hello, world'.
They'll have some sort of example, an aggregate 'car' object containing references to other objects like an 'engine', 'wheel', 'radio', etc. And it seems really neat and sensible, as they present it. But in practice it's not like that at all, and you end up with people not even able to write a game like tetris or pong without crazy monolithic manager classes and singletons all over the place.
Logged
MorleyDev
Level 0
***

"It is not enough for it to just work"


View Profile WWW
« Reply #18 on: September 20, 2014, 04:29:14 AM »

I found that I nowadays I tend to just use classes as a way to achieve partial functions where the full and collapsed specification are still explicit. The constructor parameters are explicit declarations of the dependencies and the function parameters are the explicit declarations of the parameters for the operation.

Not sure if this is just a natural evolution, a result of practising TDD pretty extensively, working mostly in the business and server-side, or just too much exposure to functional programming, but I find it works very well for keeping relevant information inside your classes and irrelevant information out.

When I call "draw(thing)" that class/function/unit really doesn't have a need to care where thing is getting drawn to, it's not responsible for that. Having to call draw(thing, screen) would be breaking this by making the unit suddenly responsible for this detail that should be irrelevant to it.

Or, when I call "getCurrentUsername()" I really don't care if it's pulling that name from a database or the cloud or a local file or memory. I just know I want the details of the current user to come from somewhere. I should't have the cognitive burden of worry about it when considering the class I'm writing.

I also tend to find an issue with OOP is the anti-patterns of class inheritance and of class-local state. Unfortunately, the way a lot of people are taught OOP emphasises the usage of these, teaching that these anti-patterns are the default way to go about things and not the exceptions they should be.

So I guess OO-itis is coding the way people are introduced to OOP, where they try and have the classes model the problem in real world terms, instead of the abstracts of functions and behaviours. That doesn't work out very well.
« Last Edit: September 20, 2014, 04:45:55 AM by MorleyDev » Logged

Garthy
Level 9
****


Quack, verily


View Profile WWW
« Reply #19 on: September 26, 2014, 07:43:49 PM »


I don't care much for the term "OO-itis". Object-oriented development is simply too useful a design methodology to be casually dismissed as if it were the actual problem. It strikes me in the same way as "screwdriver-itis", where someone who has done most of their work hammering in nails can't see what the fuss about screwdrivers is all about.

However, you can take object-oriented design too far. I think the tipping point is when you lose the elegance of the solution through pointless application of the methodology in order to reach a quasi-ideal object-oriented design.

For example: When you need a three-element position vector (ie. something with an x, y, and z), and your design involves a virtual factory that produces objects that, through virtual calls, allows you to extract each of the elements (each of a suitable polymorphic type, of course), the number of which can also be determined by a call, and the names for each element (eg. "x") can be queried individually on a per-object basis. Here, you've taken a good thing, and applied it to develop a heavily object-oriented and infinitely flexible solution, but one that is also an inelegant, impractical, unmaintainable, and obstructive monstrosity. Sure, there will be times where this is actually the *right* solution, but most of the time it will not be.

Something to be aware of is that with low-level development, or small problems, object-oriented design can be an impediment; whereas for higher-level development, or massive problems, it can be a boon. This leads to a couple of problems: You have people who have only worked on small or low-level systems making some big assumptions about object-oriented methodology being a toxic thing, and trying to impress on others how obstructive it is; and people who have only worked on academic, large, or high-level systems making some big assumptions as to how beneficial it is for lower-level development, and then trying to "educate" on a methodology that is fundamentally flawed in that application domain.

There are exceptions, of course, as always.

Another thing to be aware of is that sometimes people have been burnt by an obstructive or expensive application of object-oriented design (sometimes at their own hand, or that of another) and have then gone on to blame the wrong thing- the concept in general rather than the specific flawed application of it.

All IMHO.
Logged
Pages: [1] 2
Print
Jump to:  

Theme orange-lt created by panic