Skip to main content
gamedev.net
🔒 Locked

Game programming and good programming practises

Started by Moose6912 Nov 22, 2004 at 6:39 AM 48 replies 6.5k views
Original Post
Moose6912
Moose6912
Are these 2 concepts rivals?As far as i know,game programming requires games to have as little resource overhead in terms of processing time taken etc etc.This will require some unorthodox programming practises that are not considered good programming practises,like not using Getter and Setter methods which uses a method to manipulate private variables since this can incur some processing overhead.Waddya think?
dave
dave
Why exactly are Getters and Setters bad coding practices, doesn't not using them mean that, in a class for example, u would have to make the member data in question, public. This would mean that this member data could be read or written too with no moderation at all.

Can i also add that this would remove the point of encapsulation.

Ive read C/C++ books that recommend this as a good coding practice.

Although i have often heard, but never reasoned out why it is bad.

Please clarify.

Ace Wins (pre-clarification) :)

You win (post-clarification) :(
MaulingMonkey
MaulingMonkey
Quote:
Original post by ace_lovegrove
Why exactly are Getters and Setters bad coding practices, doesn't not using them mean that, in a class for example, u would have to make the member data in question, public.

Getters and setters in effect make the variable public, negating the entire purpouse behind the implemenation of private:. Well, that's the theory. Usually, it's a symptom of bad naming practice (std::vector does not have SetSize and GetSize - it has resize and size), or worse, not truely encapsulating the data appropriately - std::vector does not have GetStartOfMemory, it has operator[] and at().

Quote:
This would mean that this member data could be read or written too with no moderation at all.


Not if you provide a different interface. Also, there is the option of at a later date switching to properties - these can be implemented with overhead using templates, or via compiler-specific keywords, if it is found out later that an original design is broken in nature.

Quote:
Can i also add that this would remove the point of encapsulation.


Getters and setters remove the point of encapsulization, many argue. They have their pros and cons in a language such as C++ which by standard has no properties. Named as such, they are usually the symptom of bad design.

Quote:
Ive read C/C++ books that recommend this as a good coding practice.


I've read tons of C/C++ books. And guess what? Just because in it's in a book dosn't make it reliable. I've had to unlearn hundreds of poor programming practices I've self taught myself from said books.

Example "C++" programs I've seen in books use, repeatedly, without ever addressing the issues they cause (no mention of how to plug security holes, no mention of naming practices to deal with the lack of type safety):
scanf(...BAAAAAAAAAAAAD. Not typesafe. Buffer overflows. Only works with builtin types.
printf(...BAAAAAAAAAAAAAAD. Not typesafe. Potentially stack-corrupting. Only works with builtin types.
fprintf(...BAAAAAAAAAAAAAD. Not typesafe. Potentially stack-corrupting. Has modifying prefix as C didn't have function overloading. Only works with builtin types.
char pie[20]; cin >> pie....BAAAAAAAAAAAAAAAAD. User dosn't even have to be TRYING to exploit the program!!! Buffer overflow CITY!!!

Much better:
cin >>...GOOOOOOOOOOOOOOOD. Typesafe. No buffer overflows. Works with user types.
cout <<...GOOOOOOOOOOOOOOOD. Typesafe. No buffer overflows. Works with user types.
file <<...GOOOOOOOOOOOOOOOD. Typesafe. No buffer overflows. Works with user types.
string pie; cin >> pie...GOOOOOOOOOOOOOOD. No buffer overflows. The user can type an entire paragraph about how good pie is, if he likes.

The problem is many of these horrible books were written when C++ was new, and almost nobody knew how to use it right, and are yet still in print for god knows why.
paulecoyote
paulecoyote
Getters and setters are useful for communicating (and martialling) state information from external sources to the class. They in fact aid encapsulation by ensuring verification is kept within the class, and data flow control remains in the class. You could do the same with functions taking inputs and outputs, but depending on language (like c# that has properities built in) you'ld just be making more work for yourself providing a non-intuitive interface.

A setter may break down a value from a string into several internal int values for example. Or a getter may take several values and combine them whenever needed. Or a setter / getter may normalise something (eg make sure strings are kept in lower case).

They are also useful for things like form designers whom, again, want to store the state of an object.

Saying that they are always bad is jumping on a particular fashionable bandwagon. They can still be useful when used correctly and with caution.
Anything posted is personal opinion which does not in anyway reflect or represent my employer. Any code and opinion is expressed “as is” and used at your own risk – it does not constitute a legal relationship of any kind.
leiavoia
leiavoia
What's bad about Get/Set-tuplets is their overuse. Newbies tend to make a Get/Set pair for every single private variable a class has. That's not the point of making them private. For most objects, private variables are just that. They reflect object internals and should not (normally) be the business of anyone else. Just like you don't need to know what kind of engine runs your car, as long as it works. You should say

Car->Start();
Car->Go();

and not

Car->GetEngine()->InjectFuel();
Car->GetEngine()->IgniteFuel();
Car->GetEngine()->MoveParts();
...etc...
Zahlman
Zahlman
Quote:
Original post by paulecoyote
A setter may break down a value from a string into several internal int values for example. Or a getter may take several values and combine them whenever needed. Or a setter / getter may normalise something (eg make sure strings are kept in lower case).

They are also useful for things like form designers whom, again, want to store the state of an object.

Saying that they are always bad is jumping on a particular fashionable bandwagon. They can still be useful when used correctly and with caution.


Methinks you are blurring the definitions here. :s

But yes. Basic principle is that a method should *do something*, and that methods should be assigned to the object that is most able to perform the code (i.e. has best access to the necessary data).

BAD:

// Of course, lots of constructor type stuff etc. is missing...class Foo {  int x;  int y;  public:  int getX() { return x; }  int getY() { return y; }}class Bar {  Foo myFoo;  public:  int getFooProduct() {    return myFoo.getX() * myFoo.getY();  }}


GOOD:

// and similarly here.class Foo {  int x;  int y;  public:  int product() { return x*y; }}class Bar {  Foo myFoo;  public:  int fooProduct() { return myFoo.product(); }}
paulecoyote
paulecoyote
... if x was price in your example - would it not make sense to have a "getPrice, setPrice" for a product is say a point of sell system?

SetPrice could have something that prevents a negative figure being input for example.

What about if a product was rebranded, like from "Jif", to "Cif" like happened here recently - or "Opel Fruits" to "Starburst"... seems like a setter for the product name (make sure it has a name, doesn't allow less then x characters in a name) and a getter to get the name would also be valid.

Another reason for studious use of getters and setters... ActiveX and COM. Want to be able to drop a product onto a form elsewhere... Getters and Setters again would be useful.

Saying they are never necessary and should never be used is being pretty closed-minded. Sometimes a class DOES need input from the outside world to change its state... getters and setters allow you to marshal this. Obviously not all private members should be manipulated by the outside world, but certainly some can legitimately be. A class that is completly sealed off with no input is pretty useless unless it's only producing the same thing over and over.

Still it's your choice, I won't say you're wrong for not using them... but you shouldn't have a go at people for using them either - when used correctly.
Anything posted is personal opinion which does not in anyway reflect or represent my employer. Any code and opinion is expressed “as is” and used at your own risk – it does not constitute a legal relationship of any kind.
b1
b1
(the original AP was me, btw)

"... if x was price in your example - would it not make sense to have a "getPrice, setPrice" for a product is say a point of sell system?"



i'd rather have Serialize(DataStream &Data) or some such, which loads/saves all that sort of internal state to/from a database (or file or whatever). but maybe that's just me.


I guess it's because i tend to treat 'Plain Old Data' and a 'Class' differently in my mind. a Class is something to be used, which takes care of its self (Player, Server, Client, BitBuffer etc). They have an internal state, loads of members etc, but only they really care about them. Calling Connect() on the Client class will change all sorts of internal data, but that's of no concern to the outside world... Where as data (drawVertex, DialogData or whatever) exist only to be data, by definition every member needs to be known to the rest of world, getters and setters validating data is fine. great. makes perfect sense to me... Just the idea of mixing a 'class' with POD seems like it's tying the interface to the implementation too closely or something, just doesn't seem right to me, but i'm sure i've done it more than once, because it was easier at the time.



i guess my point is: i don't know what i'm talking about, just do whatever makes sense for you, and i'll do the same :)



for what it's worth, i use 'struct' for POD... to make it clearer that it's just 'data'.. i'm sure some veteran will come and chastise me for it, but whatever :)
Yohumbus
Yohumbus
I would have to say that getters and setters are often necessary (which does not say that they are never unnecessary). The logic here is that they do provide for the best encapsulation in most cases. The purpose of encapsulation is so that if a class needs to be reeplemented it can do that without ever changing its interface. In the case of getters and setters if that ever needs to happen then the function can simply be changed to affect whatever new datatype that setter set.
ASCII stupid question, get a stupid ANSI
Extrarius
Extrarius
The only time getters and setters are pointless is when you provide both of them and the functions do nothing more than set a value or return it. If you only provide one of the two, or you do something in either function, they are worth having.

You have to be careful about overgeneralizing.
"Walk not the trodden path, for it has borne it's burden." -John, Flying Monk
MaulingMonkey
MaulingMonkey
Quote:
Original post by paulecoyote
... if x was price in your example - would it not make sense to have a "getPrice, setPrice" for a product is say a point of sell system?


Not as simply as that, no. You're describing an Item Mantinence interface which is not nearly advanced enough - there need to be options for setting discount levels, changing the "base" discount, store-specific level changes and global changes - and your functions are inappropriately named for this, as "setPrice" definately does not indicate "what price", nor does "getPrice". Item mantinence can be done using LookupItem() and WriteItem(), if both deal with structures - making the get/sets superflous here.

On the other hand, if you're describing the interface to be used by the checkout section of a program - it's too complex. Checkout software does not need to be able to alter the price of an item globally. Prehaps on a case-by-case basis, but that would be implemented in a class such as "BillItem", not "ProductData". Here, we could just have the product data be POD:

struct bill_item{    string item_id;    string item_description; //In case comments need to be attached.    int quantity; //negative for returns, 0 for uncounted psuedo-items    price_t price() const; //actual end price    price_t base_item_price; //base item price, factoring in direct per-item discount. Get/set privilages needed anyways, dosn't need to update price() since that's a calculation instead of a variable.    double discount; //percentage, factored into base_item_price in price(), Get/set privilages needed anyways, dosn't need to update price() since that's a calculation instead of a variable.    //Rationale:    //This allows for tracking transaction-price seperately    //from item price, which may change after the item has    //been bought. Also allows for case-specific discounting.};


price() here is a factor of a multitude of values, and it makes no sense to be able to directly set the price of the line. Thus, there is only a get-style function.

base_item_price, discount, and quantity here are simple values - plain old data (POD) - and have no direct dependance to anything. Although base_item_price is initialized with the original default item sale price, it is independant from thereon after. If this were not the case, transactions would not be restorable at a later date if the price changed.

Quote:
SetPrice could have something that prevents a negative figure being input for example.


That'd rule out the use of negatively-priced "items" representing scannable cupons, rebates, or the like, as is frequently done in POS software.

Quote:
What about if a product was rebranded, like from "Jif", to "Cif" like happened here recently - or "Opel Fruits" to "Starburst"... seems like a setter for the product name (make sure it has a name, doesn't allow less then x characters in a name) and a getter to get the name would also be valid.


I'd prefer to see for a COM style interface on the cashier end:

LookupItem( id );

Which then returns a simple structure containing the data.

And then the following function for item mantinence:

WriteItem( ... );

Of course, I have the advantage of experience here, as I have done some research into replacing the POS software at the toystore I work for. I mean POS in two ways: Point Of Sales, and Piece Of Shit.

The program is buggy, we had to upgrade TWO versions just to become Y2K compliant, at a HUGE cost (thousands of dollars) for a SINGLE OUTLET. The software is incomplete (many menu items display "this feature is not yet implemented"), buggy (we crash to shell consistantly with some features, a bug in the code recently rewrote our backstock inventory numbers with on-the-shelf numbers, forcing yet another manual recount). Other wonderful things that have shown up are untypeable characters in item descriptions, as well as newly created items having -250 items in stock supposedly.

Did I mention you loose your entire sales history for an item if you change the item number through the item mantinence screen? Instead, you have to go to a tacked on menu ("SUM/IT Utilities") way back at the main menu (aka not accessable from the middle of a transaction, as is possible with items - which is done to allow the fixing of bogus information in the computer). These are symtoms of badly written interfaces to the item database, in a manner fitting exactly with things like: GetItem/SetItem (the recent bug was fixed with the inventory by making some code aware of the fact that we used multiple "stores" to indicate different areas of the same outlet - 01 is on the shelves, 02 and 03 for in-store backstock, and 04 for offsite storage)

For the hell of it, I should also mention this piece of crap is written in COBOL.
paulecoyote
paulecoyote
MaulingMonkey> Whatever, I'm not disparaging your choice in programming style. If it works for you, fair enough. You made the simple example more complicated, it was more of an analogy then a proper system I was thinking of. But fair enough, I'll roll with it, and re-justify my thinking on decent usage of getters and setters.

You could use inheritance properly, have "Item" as a base class - and override the various methods and safe guards in "Item" for "Coupon". I would say that "Coupon" is probably actually a specialisation of "Discount"... and is different from an item. In fact depending how you assigned responsibilty and drew your concept diagram, I suppose you could even see it as it is really a form of "Tender", with it's sibblings "Cash", "Credit", etc.

I'm trying to keep Item light-weight here, an Item does not need to know anything about the shop it's in... a Shop is a completely different concept. It also doesn't need to know who sold it, that would be another concept... a Receipt's - job.

Again, to look up all the stock in a store, surely a concept for Store would have the contractual obligation to know what items are in stock - if any discounts should be applied. Discount should again be it's own concept (class) away from item because it's life-time differs from an Item and is not even necessarly tied to any particular number or type of items either.

So going back to the point of all this - for a lightweight class there is nothing wrong with getters and setters if used correctly - it's just marshalling stuff in and out of a class.

It shouldn't be up to the item to worry who is changing it's price is why. That would be an audit issue - and an audit is a complicated concept in itself, as would checking any kinds of rights to check an item.


Original post by MaulingMonkey
struct bill_item{    string item_id;    string item_description; //In case comments need to be attached.    int quantity; //negative for returns, 0 for uncounted psuedo-items    price_t price() const; //actual end price    price_t base_item_price; //base item price, factoring in direct per-item discount. Get/set privilages needed anyways, dosn't need to update price() since that's a calculation instead of a variable.    double discount; //percentage, factored into base_item_price in price(), Get/set privilages needed anyways, dosn't need to update price() since that's a calculation instead of a variable.    //Rationale:    //This allows for tracking transaction-price seperately    //from item price, which may change after the item has    //been bought. Also allows for case-specific discounting.};


[/quote]

The whole point about classes and objects is to split the domain up into lots of concepts that have very clear contracts and look after the data they have in charge well. A item that keeps track of a base price, a discount price... what happens when you want to add another sort of discount? Say if you want to add a student discount to item, perhaps an OAP one... a special wednesday one... why on earth would you want to add a special case to each item? What if the discount is 10%... means your storing 10% against every single item you have. Or what about no discount, you're storing 0% for every item, what's the point in that?

(Looks like you're using a struct anyway, so everything is public by default. If it's a struct and you're just using it as a data store and exposing all the internal workings for anything to directly manipulate, then it's just data and doesn't really need to be a class at all, just data. Meaning you can keep all your functions external - like your COBOL I guess)

Better to keep Discount seperate - as stated above. A discount could keep a list of item ids, item types, or whatever that it is applied to... when it can be applied... lifetime of discount, etc. What a waste if each item had to keep track of things like that.


And that posturing about COBOL and all that POS stuff? It's not like I'm a newbie straight out of university either you know, my software is used many thousands of times a day across libraries and learning centres all over the uk. It's mature, stable and gets the job done.

So anyway, getters and setters CAN be used sensibly when contracts of the classes in the system are clear, and are backed up with more complex functions for the more complex business logic. They can provide a non-bespoke standardised method (if named consistantly) of getting to things you would expect to be accessible in a class, and a way of automatically ensuring values kept in a class are valid. If used properly they are very different from being public variables in a class structure.

I noted you did not have a go about their usefulness in COM and form designers, so I guess you concede that at least even if the rest of it gets your back up.

[Edited by - paulecoyote on November 24, 2004 7:31:58 AM]
Anything posted is personal opinion which does not in anyway reflect or represent my employer. Any code and opinion is expressed “as is” and used at your own risk – it does not constitute a legal relationship of any kind.
johnnyBravo
johnnyBravo
so say i got a class which jus has information like:
x
y
z
width
height
depth

etc etc

should i not use getter/setters?

I find it quite annoying to have to getsets on classes like these.

ALso note a class like that only exists to hold that information
paulecoyote
paulecoyote
Quote:
Original post by johnnyBravo
so say i got a class which jus has information like:
x
y
z
width
height
depth

etc etc

should i not use getter/setters?

I find it quite annoying to have to getsets on classes like these.

ALso note a class like that only exists to hold that information


Don't use them if you don't need too. If you wanted to have some kind of something that stops width and height being negative then getters and setters might be useful.

If it's just currency between related functions that will do that anyway, then there's no point to using them.

I'm saying if used properly they can be useful - and just because getters and setters may be out of fashion for some, it doesn't mean that they don't have their place. I'm in no way advocating them for every situation, every class and every private member variable.

Saying all getters and setter are evil and not good is a gross oversight to the purpose in which they were orginally made for.

Also note that a class that only holds information isn't a "class" in design speak, it's simply a datastore. A well defined class is a concept that has data that is internally looked after, has functions that make it useful to tell it to process that data, and possibly properities/attributes (using getters and setters perhaps) that may provide readonly, writeonly, or read/write access to some of those properities.

For example for a person class might have setHat. setHat might first check that it's already wearing a hat, take it off properly, then put on the new hat stated by value. getHat might take off the hat and give it to whomever is asking for it. Things are being done in the background by the getters and setters, but you don't care because you're just either telling that person to put on a hat or give a hat back. If this Person was a control on a form inside a form designer, all that complexity is wrapped up in a single call. It's state can be saved and restored via the getter and setter.

Conversly, you have a triangle - a,b,c. It's not tied to any particular co-ordinate system, so it can have negative numbers. Point of getters and setters? Zero. Unless you are providing an interface for a form designer, activex control or whatever.

If you were making a specialist triangle, say a right-angled triangle - then when a, b, or c is set then the triangle must check itself if that change has made it an invalid right-angled triangle. It can then resize the other points or throw an exception. Here a function would probably be more appropriate.

It's all about using the right tools for the right jobs... and if you forbid yourself getters and setters you are depriving yourself of a potentially empowering way to program. On the flip side if you overuse them you can make something that's completly unmanagable and bloated - something that has no advantage over just making your private member vars public.


[Edited by - paulecoyote on November 24, 2004 10:44:43 AM]
Anything posted is personal opinion which does not in anyway reflect or represent my employer. Any code and opinion is expressed “as is” and used at your own risk – it does not constitute a legal relationship of any kind.
Fruny
Fruny
Quote:
Original post by johnnyBravo
...

should i not use getter/setters?

I find it quite annoying to have to getsets on classes like these.

ALso note a class like that only exists to hold that information


Make it a struct and directly access the (public) data members.
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Max_Payne
Max_Payne
Accessors and mutators ("Getters and setters") are essential for some classes. Example:

class CBaseEntity{public:    CBaseEntity();    virtual ~CBaseEntity();    virtual void SomeFunction1();    virtual void SomeFunction2();    void SetPosition(const CVector3& Position) { m_Position = Position; }    void SetVelocity(const CVector3& Velocity) { m_Velocity = Velocity; }    void SetOrientation(const CQuaternion& Orientation) { m_Orientation = Orientation; }    const CVector3& GetPosition() const { return m_Position; }    const CVector3& GetVelocity() const { return m_Velocity; }    const CQuaternion& GetOrientation() const { return m_Quaternion; }    unsigned int GetIdNumber() const { return m_IdNumber; }private:    CVector3 m_Position;    CVector3 m_Velocity;    CQuaternion m_Orientation;    unsigned int m_IdNumber;};


Here, position, velocity and orientation frequently need to be played with. But the Id number should not be changed outside of the entity code. You could make position, velocity and orientation public, but thats simply less safe and uglier. As far as I'm concerned, however, there is no perfect use-it-everywhere solution. If you want to use accessors, you should know what you're doing.

Looking for a serious game project?
www.xgameproject.com
johnnyBravo
johnnyBravo
Quote:
Original post by Fruny
Quote:
Original post by johnnyBravo
...

should i not use getter/setters?

I find it quite annoying to have to getsets on classes like these.

ALso note a class like that only exists to hold that information


Make it a struct and directly access the (public) data members.



Just curious, say I've got a struct that is to be used globally in my project, should i give it a header file?

As I usually give a header/source file to all my classes
andrewk3652
andrewk3652
Just my $.02 here.

If you have an object that has data, and performs some kind of operation on that data, then you have a class. Write it as such.

If you have an object that has data, but doesn't perform any kind of operation on said data (other than setting or returning it), then you have a structure of data; and you should instead write it as a struct or record (whichever suits you).

If you have a class that overall fits the first definition but has some members that fit the second definition, and you find yourself needing to CONSISTENTLY get/set the data, you might consider pulling those out, putting them into a struct, and making the struct a public member of the class.

Use the unix design philosophy - break everything down into its smallest, but at the same time most functional and useful component possible, and combine when necessary.
=========================Buildium. Codium. Fragium.http://www.aklabs.net/=========================
LucidIon
LucidIon
Moose6912: An optimising compiler should inline the get/set functions. It won't make any performance difference. A programming practice normally considered bad, which can increase performance, is the use of goto's - which can be quite useful.

Without goto

Event event;int run = 1;while(run) {     event = EventHandler.poll();     switch (event.type) {         case Event.EXIT: run = 0;         // other events     }}


With goto, the code is easier (IMO) to follow and more efficient (no need to test run each time round the loop).

Event event;while(true) {    event = EventHandler.poll();    switch (event.type) {        case Event.EXIT: goto exit_event_loop;        // other events     }}exit_event_loop:


Anonymous Poster: I don't really see how the second bit of code isn't using get/set functions, it looks pretty much equivalent to:

// using get/set Character char = new Character(Direction.EAST, Speed.WALKING);// alter the datasetSpeed(Speed.RUNNING);// do somthing with the dataswitch(getSpeed()) {    case Speed.WAIT: System.out.println("wait"); break;    case Speed.WALKING: System.out.println("walking"); break;    case Speed.RUNNING: System.out.println("running"); break;


Not using get/set functions would presuambly something like:

// avoiding get/setCharacter char = new Character(Direction.EAST, Speed.WALKING);// alter the datachar.run();// do somethingSystem.out.println(char.speedAsStr());


This is probably extreme cases for both. Neither is ideal. the first breaks encapsulation, the second needs loads of functions.
A hybrid would be better.:
// hybridCharacter char = new Character(Direction.EAST, Speed.WALKING);// alter the datachar.setSpeed(Speed.RUNNING);// do somethingSystem.out.println(char.speedAsStr());

-- Jonathan
Zahlman
Zahlman
Quote:
Original post by LucidIon
Moose6912: An optimising compiler should inline the get/set functions. It won't make any performance difference. A programming practice normally considered bad, which can increase performance, is the use of goto's - which can be quite useful.

Without goto


Event event;int run = 1;while(run) {     event = EventHandler.poll();     switch (event.type) {         case Event.EXIT: run = 0;         // other events     }}


Quote:
With goto, the code is easier (IMO) to follow and more efficient (no need to test run each time round the loop).


Event event;while(true) {    event = EventHandler.poll();    switch (event.type) {        case Event.EXIT: goto exit_event_loop;        // other events     }}exit_event_loop:


That's an ugly sort of optimization to be doing. To handle multiple levels of breaking, I usually refactor to create a method:

// How I would probably actually write this particular example:while(process(EventHandler.poll())); // no bodybool process(Event e) {  switch (e.type) {    case Event.EXIT: return false;    // other events  }  return true;}// Another way which actually illustrates using the return to// break multiple levels:processEvents();void processEvents() {  Event event;  while(true) {    event = EventHandler.poll();    switch (event.type) {      case Event.EXIT: return;      // other events    }  }}


OK, probably even slower, but probably not important in this example ("event handling" suggests an I/O bound task).

Of course, if the Event class were one I got to write myself, I'd probably make use of polymorphism instead of a switch statement :/

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.