• Advertisement
Sign in to follow this  

Classes: Possible to overdo it?

This topic is 3474 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi there, I'm a game development n00b. I'm not so new to programming: I enjoy C, C++, and x86 Assembly. I've written tons of hobby projects, and I'm soon going to uni to become a computer hardware engineer. Well, where do I start...? My question is when does wrapping everything in classes become a hindrance? I've seen a lot of code where game developers wrap everything--and I mean EVERYTHING--in an object. They use singletons instead of a separate CPP or C files with global functions and state variables. Even for things that seem procedural in nature, they somehow manage to wrap them in some object system. My question is also if it's generally frowned upon to use a less complex architecture when writing game code? Like, say, if I showed some of my portfolio to a developer; suppose it's a fully working game; would they not hire me until I take the neater, more painful path? Because, I dunno, for some segments of code it seems less useful and intuitive to wrap them in objects. It seems a lot of time could be saved if I wrote some parts of the game in traditional code. It sometimes feels like using a chainsaw to cut a tomato because everyone else does. Well, thanks.

Share this post


Link to post
Share on other sites
Advertisement
Many people make the mistake of believing that putting everything and anything into a class makes them good, OOP-driven programmers. Rather - you must use the paradigm that is most suited for what you need to do.

Share this post


Link to post
Share on other sites
Yes, excessive OOP is a common error. It is often caused by the infatutation many programmers have with "buzzword oriented programming".

OOP clearly has it's uses, but pesonally I try to follow the following rules of thumb:

If I cannot identify a proper invariant for a type, then there's rarely any need for making it an abstract datatype.

I would generally rather make something data-driven than build a class-hierachy.

The proper abstraction for a side-effect free operation is the function not the method. Or put in another way: A types methods should be the minimum set of state changing operations, not all procedures somehow relating to a type.

Share this post


Link to post
Share on other sites
The difficulty with OOP is not in the use of objects, but in designing a proper system, and classes are one of the tools to assist you in that.

In my opinion, OOP really starts to shine in large projects with multiple developers working on it, but it doesn't hurt to show your understanding of OOP in relatively small demos. But don't just "wrap" your procedural code in classes: demonstrate common sense and apply patterns where appropriate. Good code is simple and flexible.

Share this post


Link to post
Share on other sites
Object usage is not object-oriented programming. In particular, there are only a handful of cases where singletons are a valid object-oriented construct, as opposed to an object-based procedural construct or an useless an deluded effort.

In practice, object-oriented programming is about making code easier to reuse at the cost of making it harder to develop. If you can't expect to reuse it with at least a minimal probability, making it an object will only waste time.

Share this post


Link to post
Share on other sites
Quote:
Original post by ToohrVyk
In practice, object-oriented programming is about making code easier to reuse at the cost of making it harder to develop.


I find that to be closer to the theory, while in practice it's really just a set of tools for (a) code organization and (b) modelling. :S

Share this post


Link to post
Share on other sites
I believe your overall question is:

"When is it appropriate to sacrifice generally accepted good programming practices?"

Quote:

Because, I dunno, for some segments of code it seems less useful and intuitive to wrap them in objects. It seems a lot of time could be saved if I wrote some parts of the game in traditional code. It sometimes feels like using a chainsaw to cut a tomato because everyone else does.


That's basically the answer: Namely, when in your judgement, your project is better served by ignoring them.

Share this post


Link to post
Share on other sites
Quote:
Original post by CreativeCombustion
My question is when does wrapping everything in classes become a hindrance?


When it becomes a hindrance; when it's more time and effort to do so (including time spent debugging/maintaining) than not.

Quote:

I've seen a lot of code where game developers wrap everything--and I mean EVERYTHING--in an object. They use singletons instead of a separate CPP or C files with global functions and state variables. Even for things that seem procedural in nature, they somehow manage to wrap them in some object system.


Singletons are a waste, but eschewing globals for globals wrapped in a class is occasionally a problem. Personally, I find that globals in a class aids in debugging access to the global. Free functions are less of a concern. The key here though is 'seem procedural in nature'. What seems procedural to you probably doesn't to someone approaching it in an OOP manner (and almost certainly doesn't to someone approaching it in a functional manner).

Quote:

My question is also if it's generally frowned upon to use a less complex architecture when writing game code? Like, say, if I showed some of my portfolio to a developer; suppose it's a fully working game; would they not hire me until I take the neater, more painful path?


It's frowned upon to hinder maintainability and extensibility. If it works though, you'll be given a good amount of credit for that. It depends on the developer and what priority they give to 'make it work' vs 'we have to maintain this thing'. In general, the game industry is a lot more lenient towards the former.

Share this post


Link to post
Share on other sites
Yes, it is totally possible to overdo it with classes. Sometimes it's just the language. For example, if you want to implement callback functions in Java(something very simple and quite fundamental), you have to wrap your function to an object(functor). In other languages, such as Python or Ruby, you don't have to wrap everything into classes, because all concepts(functions,modules,"primitives", even classes themselves) are already objects. Other times, it's the programmer, who thinks that, in order to write proper "OO" code, he must put everything inside a class, even when it's not necessary. If a function is related to a class but can be non-member(it doesn't need acess to the internals of the class and it's not virtual), then it should be. It actually increases encapsulation, because you have one less block of code that depends of the implementation of the class.

Share this post


Link to post
Share on other sites
You should not mix OO and procedural methods of programming. They are completely different ways of thinking. There are few rules on what you should be a separate class, and what should be merged.

I'll see if i can find them.


A lot of object orientated languages do tend to have a lot of classes. If your finding your just writing the same procedural code, and putting it in a class. You have probably designed it badly in a oo way.

Share this post


Link to post
Share on other sites
Quote:
Original post by Alastair Gould
You should not mix OO and procedural methods of programming.


I respectfully disagree.

Share this post


Link to post
Share on other sites
This is my University education speaking, We've always been taught this.

You also have to remember that you can do OO without classes i.e c. It the way its designed is OO.

Share this post


Link to post
Share on other sites
Quote:
Original post by Telastyn
Quote:
Original post by Alastair Gould
You should not mix OO and procedural methods of programming.


I respectfully disagree.

Seconded.

In a multi-paradigm language, like C++ for example, you have the freedom to select the appropriate tools and mindset from a variety paradigms (procedural, OO, generic, functional, aspect, ...) to most cleanly solve the problem at hand.

Share this post


Link to post
Share on other sites
Trying to design a program formally on paper you will realise designing procedurally has completely different steps and methods than OO.

I used to think that OO and procedural methods can mixed, but now being taught formally has taught me why this can be a bad method.

The diagramming and layout of programs is completely different for a start.

Functional programming can be mixed in well. I've always written most functions/methods like that.


But oo and procedural completely change the big picture of the program.

Share this post


Link to post
Share on other sites
I came out of school about two years ago and have been working a computer game programmer during that time. In this time I have changed my way of looking at OOP a lot. Let me take and example of something that seems to be a common example of OOP programming

// C++ that is not tested in a compiler

class Animal
{
public:
virtual ~Animal() {}
virtual void cry() = 0;
};

class Dog : public Animal
{
public:
void cry() { std::cout << "Bark!"; }
};

class Cat : public Animal
{
public:
void cry() { std::cout << "Mjao!"; } // Swedish cat sound
};

After I gained some experience i realized how bad this really is! Virtual functions are the very core of OOP programming. At least in C++. Still there is a much better solution:

class Animal
{
public:
Animal(const std::string& cry) : m_cry(cry) {}
void cry() { std::cout << m_cry; }

private:
std::string m_cry;
};

The code has now become data driven instead of hard coded in a class hierarchy. There is no inheritance, only information. You can imagine how easy it is to make the Dog and Cat and other animals using a text file without touching the code.

It is often said that OOP is about hiding information but I think this is wrong. The point is to hide the way that information is stored. An std::vector is not hiding the information that you put inside it but it makes the handling of the data easier and safer. When you are going to save information to a file, or send over a network, transmit to another thread, or even display in a GUI, then you need to handle data. Preferably through some interfaces to hide implementation details.

There are many problems that just don't make sense as objects. For example the sin() function or a function that splits a string at the spaces. Even Java has static functions. The static functions are procedures and the class keyword is only used as a namesapce. Procedures are necessary.

I think my point is that you should use OOP to make plugins or frameworks or keep invariants (like std::vector). Use OOP when it is useful. Don't care what OOP programmes says that have just learned what design patterns are and think that they are smarter than others. I call them OOP snobs. Use both OOP and procedural code. If that makes you _not_ getting a job at a company I think you deserve better places to work at anyway! A company where programmers likes to get real work done.

I can write more because I am actually quite upset about that OOP has been so hyped and taught in the wrong way. At least in my school. Not that it is bad... But this is enough for tonight.

Share this post


Link to post
Share on other sites
@jonathanjansson: Whilst you're largely right, just some comments...

Quote:
Original post by jonathanjansson
Virtual functions are the very core of OOP programming. At least in C++.
Virtual functions are the core of polymorphism in C++. Polymorphism is just one aspect of OOP, others include encapsulation, modularity, inheritance, abstraction, SRP, LSP OCP and ISP.

Quote:
It is often said that OOP is about hiding information but I think this is wrong. The point is to hide the way that information is stored.
This is encapsulation.

Quote:
An std::vector is not hiding the information that you put inside it but it makes the handling of the data easier and safer.
Of course a std::vector is really a better example of generic programming rather than object oriented programming. Also, a std::vector does hide the implementation details of a dynamic array and associated memory management.

Share this post


Link to post
Share on other sites
Quote:

Trying to design a program formally on paper you will realise designing procedurally has completely different steps and methods than OO.


So I can write an actual, real program that mixes different paradigms and solves every problem using the optimal solution, but I can't design it on paper? If that's the case, then what's wrong is the way you design it on paper, not the other way around.

Quote:

I used to think that OO and procedural methods can mixed, but now being taught formally has taught me why this can be a bad method.


Why?

Share this post


Link to post
Share on other sites
Quote:
Original post by dmatter
@jonathanjansson: Whilst you're largely right, just some comments...

Quote:
Original post by jonathanjansson
Virtual functions are the very core of OOP programming. At least in C++.
Virtual functions are the core of polymorphism in C++. Polymorphism is just one aspect of OOP, others include encapsulation, modularity, inheritance, abstraction, SRP, LSP OCP and ISP.

Quote:
It is often said that OOP is about hiding information but I think this is wrong. The point is to hide the way that information is stored.
This is encapsulation.

Quote:
An std::vector is not hiding the information that you put inside it but it makes the handling of the data easier and safer.
Of course a std::vector is really a better example of generic programming rather than object oriented programming. Also, a std::vector does hide the implementation details of a dynamic array and associated memory management.


Thanks for the reply. I am not familiar with those abbrevations, maybe if you spell them out i will know them?

I think a problem is that OOP is not very well defined. OOP means different things for different people and programming languages, which makes the discussion more difficult. I agree that inheritance and abstractions are typical OOP features which in C++ are implemented using virtual function.

I think that OOP takes too much credit for what really is sane programming practice. For example encapsulation. Any good C API uses opaque structures and a few functions. An opaque struct is in my opinion even safer than a private section in C++. The reason is that a pointer is always safe to keep on the stack but keeping object itself is not if the members would change.

Another iteresting thought it that if you would replace all private: and protected: declarations with public:, would the code not be object oriented anymore? If restricting access to variables is not an OOP feature, then I think that C++ does not bring much more than virtual functions (which enables polymorhism, inheritance etc.) to the laguage from an OOP perspective. Templates and RAII, better type safety are not that related to OOP I think.

I think that std::vector is an example of both encapsulation and generic programming which proves the point that most people here tries to make. OOP is only one technique among many. It is a good way but not the only way. Generic programming is another good way. So is procedural and functional and relational etc.

Share this post


Link to post
Share on other sites
Quote:
Original post by jonathanjansson
I am not familiar with those abbrevations, maybe if you spell them out i will know them?

Sure can [smile]

Single Responsiblity Principle - SRP
Open Closed Principle - OCP
Liskov Substitution Principle - LSP
Dependancy Inversion Principle - DIP (missed this one last time)
Interface Segregation Principle - ISP

I'm sure some of these can be reinterpreted for other paradigms too, but you will find these principles are frequently associated with Object Oriented Programming (*sneakily borrows links from ToohrVyk*).

Quote:
Another iteresting thought it that if you would replace all private: and protected: declarations with public:, would the code not be object oriented anymore?

I'd say it would be less (not!) encapsulated and so less object oriented.

Share this post


Link to post
Share on other sites
Quote:
Original post by jonathanjansson
Quote:
Original post by dmatter
Polymorphism is just one aspect of OOP, others include encapsulation, modularity, inheritance, abstraction, SRP, LSP OCP and ISP.

Thanks for the reply. I am not familiar with those abbrevations, maybe if you spell them out i will know them?


I assume you refer to those. They are the Single Responsibility Principle, Liskov Substitution Principle, Open-Closed Principle and Interface Segregation Principle. These terms should all work fairly well in google.

Quote:
I think a problem is that OOP is not very well defined. OOP means different things for different people and programming languages, which makes the discussion more difficult. I agree that inheritance and abstractions are typical OOP features which in C++ are implemented using virtual function.


This much is very true. I'm not even really sure what to make of it when someone tries to apply "OO" as an adjective to a process (/idiom/design pattern/segment of code).

Quote:
I think that OOP takes too much credit for what really is sane programming practice.


It is rather the people selling OOP languages (*cough* Sun) and people branding themselves as "consultants" who assign that credit, I think. :)

Quote:
For example encapsulation. Any good C API uses opaque structures and a few functions. An opaque struct is in my opinion even safer than a private section in C++.


In C++ we use the 'pImpl idiom' to gain the same advantage of hiding the private data of a class. C++ lets us omit that to avoid indirection where we don't want it, and people end up being lazy (also because the ownership semantics can be tricky). However, this pointer-indirection can come free with the purchase of the pointer-indirection needed for polymorphism, with some smart design - which will also look after ownership semantics. The net result is that you just put one of these guys in the private section of a "wrapper" class, which has non-virtual functions which delegate to the implementation. Voila, all the power of polymorphic objects without the babysitting.

Quote:
If restricting access to variables is not an OOP feature, then I think that C++ does not bring much more than virtual functions (which enables polymorhism, inheritance etc.) to the laguage from an OOP perspective. Templates and RAII, better type safety are not that related to OOP I think.


Many "OO" languages don't bother with this restriction. It's assumed that the programmer will treat documented functions as public and everything else as protected or private. (Decoupling the base and derived classes from each other is typically not worth the effort; however, redesigning to avoid inheritance completely often is.)

And yeah, C++ really doesn't bring much OO to the table. But it brings enough to let you write things with typical-of-OO syntactic sugar, and even make it work (until you hit a wall and realize how low-level the language still is).

Quote:
It is a good way but not the only way. Generic programming is another good way. So is procedural and functional and relational etc.


Of course. (I'm used to 'imperative' rather than 'procedural' and 'declarative' rather than 'relational', but I think we're thinking of the same things.) You pick the right tool for the job. C++ lets you do that, and is one of its strengths: the library provides enough of an attempt at "functional" support, that you can use OO practices to describe your data structures, FP ones for algorithms, and imperative code for "glue".

Share this post


Link to post
Share on other sites
Its certainly possible, and even common among the less experienced, to over-use classes and inheritence in their designs. However, in my own experience, I find that objects ultimately make the project more understandable and maintainable by organizing discreet concepts into a single package. This does not mean, necessarily, that everything is an object, member or method (which is not the hallmark of real OOP) however; the appropriate and judicious employment of free functions, POD types and other techniques go far in creating an elegant software design.

It is true that designing and constructing a program with proper OO practices is more difficult than their procedural counterparts because procedural designs are simply more forgiving of design mistakes -- In simpler terms, a program in a procedural language like C is very often more fluid in the face of design mistakes, while an object-oriented language like C++ has a greater tendency for the inexperienced to paint themselves into a corner... That said, the nature of OO languages is that if you've gotten yourself into that much of a predicament, its nearly always a sign that the design itself is flawed. I like to think of this as a form of "tough love" in some ways... Object oriented languages make it apparent when there's a design flaw and forces your hand in dealing with it -- Procedural languages tend to be happy enough to let you bumble your way through to the end if you're stubborn (or thick) enough not to notice the more subtle hints it gives you.

I think what we're all trying to say is that the judicious and correct use of the OO paradigm has great benefits in modeling many (if not most) aspects of most software (eg concepts, ownership, responsibility). It's tools can, and probably should, live side by side with other tools from other paradigms in a good design.

With experience, proper OO designs will come more naturally to you, and you will begin to design fewer chainsaws for those tomatoes when a knife will do just fine. The trick with C++, and OO in general, is to not over-engineer your solutions and to embrace the single-responsibility principle.

Share this post


Link to post
Share on other sites
Quote:
Original post by linternet
I believe your overall question is:

"When is it appropriate to sacrifice generally accepted good programming practices?"


Yes, you summed it up succinctly. Thanks. <Grin>



Thank you for all your help guys. I was pretty much worried that my code had to be super object oriented before I'd ever be taken seriously by a developer. I like a more pragmatic approach to code.

For some things, like say, a physics (particle motion and collision) system I can see why using polymorphism can save a lot of wasted effort. Simply make a base class like this:


class iParticle
{
...
virtual void OnCollide() = 0;
...
};

And then inherit from it, making the collision detection code not care what type of particle it is, but just that it is a particle, and then I'll be able to have particles with extensible logic by inheriting from the interface.

But other types of things where treating systems as a bunch of objects just don't make much sense, like a global command interpreter, whose only job is to process text input and call all the various global subsystems. The interpreting logic itself just needs to be a global function (by what I have in mind), and making it a functor seems like an overkill because I'm pretty sure I won't really expand on this much--I'd rather make the program ran on external data like an ini file than to have an entirely big hierarchy of classes and stuff to represent commands that I won't really use because I'm the only programmer in the project. Yes, it's neat and elegant, but it's also more work than I need specifically for my simple game in mind. <Grin>

Well, anyway, thanks guys.

P.S. How do I use the emoticons? Can't find the menu anywhere.

Share this post


Link to post
Share on other sites
Quote:
Original post by CreativeCombustion
For some things, like say, a physics (particle motion and collision) system I can see why using polymorphism can save a lot of wasted effort. Simply make a base class like this:


class iParticle
{
...
virtual void OnCollide() = 0;
...
};

And then inherit from it, making the collision detection code not care what type of particle it is, but just that it is a particle, and then I'll be able to have particles with extensible logic by inheriting from the interface.
As it happens there may be a better designs for that.
More specifically, by separating out the physics collision system from the repsentation of a particle. Particle systems tend to be very performance unfriendly too, and calling many virtual functions on individual particles may not be the best idea.

Quote:
But other types of things where treating systems as a bunch of objects just don't make much sense, like a global command interpreter, whose only job is to process text input and call all the various global subsystems. The interpreting logic itself just needs to be a global function (by what I have in mind), and making it a functor seems like an overkill because I'm pretty sure I won't really expand on this much
Bear in mind, though, that if your command interpreter needs to retain any state, like references to the subsystems or some FSM state, then sticking it into a class is probably the best thing to do. The other options are to use global or static variables and they just aren't as good.

Quote:
P.S. How do I use the emoticons? Can't find the menu anywhere.
Yeah there's no menu but the FAQ has a table of BBCodes. [smile]

Share this post


Link to post
Share on other sites
One question you have to ask is, what do you gain by using OOP or OOD? Some things can of course be way over engineered. Both procedural techniques and object oriented techniques have there uses.

Look at like this, the Windows and Linux operating systems are almost entiely C. The Windows API is C based. MFC (Microsoft Foundation Classes), are just wrappers around the Windows API.

I am a Senior in Computer Science, and I have had classes in Object Oriented design where we have created small - medium sized team based projects, using Java. In other classes, like my System Programming and Computer architecture class, was C and Assembly.

Just remember, that when you start working somewhere, they are most likely already going to have a code base to work from, and it has probably been around the block more than once.
Where I work, everything is written in C/C++ and is based on MFC. The high level portions, the window system etc.. uses object oriented techniques and methods, while the low level stuff is basically straight up C. (I work on writing and maintaining device drivers for Programmable Logic Controllers).

In the end, these are all just tools. Part of being a craftsmen is knowing how to use the appropriate tool for the appropriate job. It doesn't sound like you are getting caught up in the land of OO, but don't get tied to strictly procedural stuff either.
Learn how to use both appropriately, and also don't get attached to a single language. Learn a new one every now and again.

Casey

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement