Jump to content

  • Log In with Google      Sign In   
  • Create Account


Why are static variables bad?


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
57 replies to this topic

#41 mike3   Members   -  Reputation: 149

Like
0Likes
Like

Posted 05 September 2013 - 03:22 PM

 

So what would be the correct approach in the example case, with the struct where a new member may be added? Where would the error be "reported" to, and in what way? More importantly, how would we know "whatever" was uninitialized, when its value as uninitialized may be undefined?

E.g.

FooContext fooContext;
fooContext.bla = <smth>; // old code that didn't know about the new "whatever" feature

// member whatever could have an undefined value, so how does this check that and "report" it, fail gracefully, etc.?
Foo *foo(new Foo(fooContext));

As like he says, it seems you need a constructor of some kind in fooContext to prevent this. Even if not one to force the programmer to pass a parameter, then at least a default one to initialize the variables so their values are all defined (perhaps so as to indicate special uninitialized states that Foo can then check for).

The ideal thing to do would be to treat the context as a volatile object and check for null on .bla before attempting to use it.
 

 

Do you mean a check on "whatever"? Since "whatever" was supposed to be the problem variable, as it was supposed to represent a "new feature" that had been added, and all preexisting code (i.e. before the feature addition) only knew of bla. The code already initializes bla. (Though I suppose maybe a check on bla would be good too, as someone could fail to init that, too) But where would this check go? If "in the code initializing the FooContext", then that means the programmer has to add it to each such piece of code, which creates a lot of duplicated checks, and more importantly, if you're doing that why aren't you just adding a proper initialization of whatever? And furthermore, the programmer adding such checks is then aware of the existence of the rest of the code, whereas kunos' scenario was that someone adds a new feature ("whatever" in this case) but isn't aware of/forgets to add the corresponding initialization to every place the FooContext is initialized. So if they are to add the checks to wherever the FooContext is initialized, then they know about those places, and so they should be adding proper initialization too, I'd think. But his scenario is that they are not so aware. It seems he wants it "robust against feature addition" or something like that. If the check goes in the constructor, then what about the concern about constructors being bloated with checks?


Edited by mike3, 05 September 2013 - 03:22 PM.


Sponsor:

#42 Satharis   Members   -  Reputation: 949

Like
0Likes
Like

Posted 05 September 2013 - 04:14 PM

Do you mean a check on "whatever"? Since "whatever" was supposed to be the problem variable, as it was supposed to represent a "new feature" that had been added, and all preexisting code (i.e. before the feature addition) only knew of bla. The code already initializes bla.

I suppose I wasn't understanding the example very well. If "whatever" is a member added to the struct later then what would it being set to null matter for old code? Unless the "old code" tried to utilize it then it would essentially be the same as the transparent addition of adding a new variable to a class your code was using, but not utilizing that particular variable.

I'm assuming the point you're getting at is, someone adds the new "whatever" variable to an object, and your code attempts to use the "whatever" object, but your coworker managing the usage of the struct that passes it to you hasn't implemented setting that whatever to something yet. In that case of course you would have to check validity of the object before you use it. If I'm getting this wrong please do re-explain it to me.
 

But where would this check go? If "in the code initializing the FooContext", then that means the programmer has to add it to each such piece of code, which creates a lot of duplicated checks

It does, but in the case of a structure it's about the same as if you require a constructor to set the .bla and the .whatever and that whatever may have been changed to a null state after construction anyway.

What it comes down to is that you have two tools to an object really. A reference, which is a guarentee that the object is at least existant even if it may be in an invalid state for your usage, or a pointer, which can be null. I think as I read more into what you're saying, what you're getting at is that by using a constructor you are forcing the caller to pass a constructed object to your structure at the time of creation of the structure, and sure, that's true. But if the structure is designed to allow that information to change you'd have to use a pointer anyway, which would indicate you have to put the "redundant" error checking anyway. So again, it's a tradeoff really. You can use the construction power of the reference but you're only really avoiding a nullptr check, and you're saying explicit to the caller "you can't change this after creation."
 

And furthermore, the programmer adding such checks is then aware of the existence of the rest of the code, whereas kunos' scenario was that someone adds a new feature ("whatever" in this case) but isn't aware of/forgets to add the corresponding initialization to every place the FooContext is initialized. So if they are to add the checks to wherever the FooContext is initialized, then they know about those places, and so they should be adding proper initialization too, I'd think. But his scenario is that they are not so aware. It seems he wants it "robust against feature addition" or something like that. If the check goes in the constructor, then what about the concern about constructors being bloated with checks?

I'd agree in the case of adding new required dependencies to an object the system of requiring a reference to be passed is more safe, I can't debate that. But like I said you're not actually arguing against having a default constructor in that case you're simply arguing that it is more clear that an object with a constructor requiring a reference is more "informative" and "complicance requiring" than an object that can be changed after construction. Even if you only had one constructor, if you gave a method to change that member at all then you would inherently have to do the same nullptr check as providing a default constructor would.

Thus at the heart of it, the only real debate here is one of style: do you prefer each object to be different and ask for what it deems to be "required" dependencies at construction, or do you make setting the information more transparent and allow a default constructor as well as the overloaded ones of the same style. It's arguably not a very big difference but it has a big effect on how the code is portrayed to others.

Like the SFML example I was using, it isn't as big a deal as you're making it out to be, in the example of the sprite object it really isn't that unintuitive to "know" that you have to set a texture to the sprite before using it, even if you construct it without passing one. However since quite a few people seem more interested in removing "dangerous" code like a default constructor I may experiment more with different mixtures of both methods in some future projects to see how it works out in real world example. My biggest problem with debates using simple class like a foo or a baz or a whatever is that they don't take things like multiple dependencies and subsystems into account or the mutable nature of a lot of game objects. Most game objects are going to have a lot of state you can set after creation, perhaps even major systems, so in that the "reference only" constructor quickly becomes an impossibility.

Edited by Satharis, 05 September 2013 - 04:17 PM.


#43 frob   Moderators   -  Reputation: 19830

Like
0Likes
Like

Posted 05 September 2013 - 04:49 PM

Clearly a constructor will not fill a dynamic job like that, even if you may use one to set an initial state.

Its kind of like:

Car myCar;
myCar.Drive(); // error message: can't drive without an engine!
Engine engine;
myCar.SetEngine(engine);
myCar.Drive(); // error message: engine is missing spark plugs
SparkPlug plug;
engine.SetPlug(plug);
myCar.Drive(); // error message: car can't drive without a fuel tank
Wouldn't that drive anybode crazy? If a car can't drive without an engine, it shouldn't be constructable without an engine. You might want to have a setter method to change the engine later, but whats the point of building/constructing it so that you can't even use it without adding all other parts?

 

Yes, and in your example you're talking about one dependency. I don't see how it is much more clear to have a constructor that is default and one that asks for an Engine, and then to have one setter for an engine as well. Literally the only difference you're making is that you're forcing an assumption that some kind of engine must be passed into the object at creation.

 

 

I've been working in games for decades, and I've never seen a 'real' game try to do what was done in the snippet above. 

 

Most games have many phases of construction for a game object.
 
There is the game object's basic default constructor.  This should generally do nothing. In cases such as serialization you are going to overwrite all the data inside the object anyway, so work in the constructor will be thrown away. That doesn't mean the object isn't fully created; think of it more like you could consider a file stream that hasn't been opened or otherwise attached to any resources.
 
There is a virtual function on game objects for when they are created. This might be used by world builders or when objects are created at run time. Do the initialization here. At this point it still should not be hooked up to any resources.
 
There is a virtual function on games objects for when they are started. This function is called after deserialization or creation. This might be after the level has loaded but before the game goes live to the player. This will also happen when objects are created at run time. Resources get requested here. Also hook up interactions and other components here.
 
There is a virtual function on game objects for when they are in the world. This lets the object hook itself up with other objects in the room or proximity. They might also set special flags such as requests to not be culled, or to set a non-standard culling distance. Some objects and triggers may not need to be placed in the world to be functional.
 
Many large games also have spatial culling. If so, there will likely be virtual functions when the object enters the simulated area and when they leave the simulated areas.
 
 
If all of those are true, there are several stages of initialization:  constructed --> created --> started --> in world --> visible.  It is possible to go back through the hierarchy as well, remove 'visible' when they are culled, remove 'in world' when they are removed from world, remove 'started' when the object is serialized or stopped for other reasons.
 
Not that any of this has much to do with shared state using static variables.


Check out my personal indie blog at bryanwagstaff.com.

#44 Satharis   Members   -  Reputation: 949

Like
0Likes
Like

Posted 05 September 2013 - 04:58 PM

I've been working in games for decades, and I've never seen a 'real' game try to do what was done in the snippet above. 
 
Most games have many phases of construction for a game object.
 
There is the game object's basic default constructor.  This should generally do nothing. In cases such as serialization you are going to overwrite all the data inside the object anyway, so work in the constructor will be thrown away. That doesn't mean the object isn't fully created; think of it more like you could consider a file stream that hasn't been opened or otherwise attached to any resources.
 
There is a virtual function on game objects for when they are created. This might be used by world builders or when objects are created at run time. Do the initialization here. At this point it still should not be hooked up to any resources.
 
There is a virtual function on games objects for when they are started. This function is called after deserialization or creation. This might be after the level has loaded but before the game goes live to the player. This will also happen when objects are created at run time. Resources get requested here. Also hook up interactions and other components here.
 
There is a virtual function on game objects for when they are in the world. This lets the object hook itself up with other objects in the room or proximity. They might also set special flags such as requests to not be culled, or to set a non-standard culling distance. Some objects and triggers may not need to be placed in the world to be functional.
 
Many large games also have spatial culling. If so, there will likely be virtual functions when the object enters the simulated area and when they leave the simulated areas.
 
 
If all of those are true, there are several stages of initialization:  constructed --> created --> started --> in world --> visible.  It is possible to go back through the hierarchy as well, remove 'visible' when they are culled, remove 'in world' when they are removed from world, remove 'started' when the object is serialized or stopped for other reasons.
 
Not that any of this has much to do with shared state using static variables.

And here I was starting to wonder if I was the only one that had objects that exist in different states and have information changed both during runtime and in different stages of setup and serialization. Guess I'm not totally crazy.

But yes, we have gone off on a bit of a tangent, though it's a good topic to bring to the attention of people because they're most assuredly going to run into it at some point in game development.

#45 mike3   Members   -  Reputation: 149

Like
0Likes
Like

Posted 05 September 2013 - 07:10 PM

 

Do you mean a check on "whatever"? Since "whatever" was supposed to be the problem variable, as it was supposed to represent a "new feature" that had been added, and all preexisting code (i.e. before the feature addition) only knew of bla. The code already initializes bla.

I suppose I wasn't understanding the example very well. If "whatever" is a member added to the struct later then what would it being set to null matter for old code? Unless the "old code" tried to utilize it then it would essentially be the same as the transparent addition of adding a new variable to a class your code was using, but not utilizing that particular variable.

I'm assuming the point you're getting at is, someone adds the new "whatever" variable to an object, and your code attempts to use the "whatever" object, but your coworker managing the usage of the struct that passes it to you hasn't implemented setting that whatever to something yet. In that case of course you would have to check validity of the object before you use it. If I'm getting this wrong please do re-explain it to me. 

 

The example appears to be: We have a class "Foo" which takes a "FooContext" to specify how to set it up. Initially, this "FooContext" contains only one parameter, "bla". Bits of code are created that have to make Foos from a FooContext, and naturally, they only set bla. But then down the road as our project progresses, someone decides now to add a new feature to Foo, which now requires a new parameter, "whatever", in the FooContext. And in how I imagine this scenario, which may or may not be how kunos imagined it, I imagine "whatever" to be a strictly "extensional" feature, i.e. it does not alter the original behavior of Foo, but extends it to add capability, that is, that the old way of using Foo should still work and not break. (The reason for this requirement is because without it, we might be forced to change the code anyway, but I'm trying to isolate the addition of the data field itself as the problem) But the programmer adding the new "whatever" parameter forgets a place where FooContexts are used and so doesn't add the proper initialization to the extensional feature whatever. The constructor for Foo now gets gibberish in the whatever field and crashes or behaves in some unpredictable ("undefined") way. It's not that the old code (here, meaning the code that makes the FooContext and the Foo) attempts to use "whatever", it's that the old code doesn't use "whatever", and because it was not updated to take "whatever" into account (meaning, it doesn't even set it to null, it doesn't do anything with it, period), causes Foo's constructor to gag.

 

So do you add the check for "whatever" not being set in to the old code, in which case it would probably be easier simply to just properly initialize whatever, or do you add the check in Foo, and regardless of the check placement, do you need to add a constructor to FooContext that ensures a stable "uninitialized" or "null" value that can be checked for? Because if you have to add the check and/or set-to-null in the old Foo-using code, then if you forget to add it there, you have a crash or other failure. To me, it seems the only way to make it robust against such omission is to put a constructor in FooContext that pre-initializes whatever to null.


Edited by mike3, 05 September 2013 - 07:22 PM.


#46 Satharis   Members   -  Reputation: 949

Like
0Likes
Like

Posted 05 September 2013 - 08:14 PM

The constructor for Foo now gets gibberish in the whatever field and crashes or behaves in some unpredictable ("undefined") way. It's not that the old code (here, meaning the code that makes the FooContext and the Foo) attempts to use "whatever", it's that the old code doesn't use "whatever", and because it was not updated to take "whatever" into account (meaning, it doesn't even set it to null, it doesn't do anything with it, period), causes Foo's constructor to gag.
 
So do you add the check for "whatever" not being set in to the old code, in which case it would probably be easier simply to just properly initialize whatever, or do you add the check in Foo, and regardless of the check placement, do you need to add a constructor to FooContext that ensures a stable "uninitialized" or "null" value that can be checked for? Because if you have to add the check and/or set-to-null in the old Foo-using code, then if you forget to add it there, you have a crash or other failure. To me, it seems the only way to make it robust against such omission is to put a constructor in FooContext that pre-initializes whatever to null.

Why would it crash on "whatever" if none of the old code uses it? You literally could have an object with two members, one being a pointer set to gibberish and one being a pointer set to a valid object and as long as you never tried to -use- the gibberish pointer it wouldn't actually do anything undefined.

#47 mike3   Members   -  Reputation: 149

Like
0Likes
Like

Posted 05 September 2013 - 08:30 PM

 

The constructor for Foo now gets gibberish in the whatever field and crashes or behaves in some unpredictable ("undefined") way. It's not that the old code (here, meaning the code that makes the FooContext and the Foo) attempts to use "whatever", it's that the old code doesn't use "whatever", and because it was not updated to take "whatever" into account (meaning, it doesn't even set it to null, it doesn't do anything with it, period), causes Foo's constructor to gag.
 
So do you add the check for "whatever" not being set in to the old code, in which case it would probably be easier simply to just properly initialize whatever, or do you add the check in Foo, and regardless of the check placement, do you need to add a constructor to FooContext that ensures a stable "uninitialized" or "null" value that can be checked for? Because if you have to add the check and/or set-to-null in the old Foo-using code, then if you forget to add it there, you have a crash or other failure. To me, it seems the only way to make it robust against such omission is to put a constructor in FooContext that pre-initializes whatever to null.

Why would it crash on "whatever" if none of the old code uses it? You literally could have an object with two members, one being a pointer set to gibberish and one being a pointer set to a valid object and as long as you never tried to -use- the gibberish pointer it wouldn't actually do anything undefined.

 

 

The way I interpreted the example, the crash occurs when Foo's constructor is invoked with the FooContext having an uninitialized whatever member, and the crash occurs when Foo's constructor is invoked and chokes.



#48 Satharis   Members   -  Reputation: 949

Like
0Likes
Like

Posted 05 September 2013 - 09:52 PM

The way I interpreted the example, the crash occurs when Foo's constructor is invoked with the FooContext having an uninitialized whatever member, and the crash occurs when Foo's constructor is invoked and chokes.

Well then that means whoever wrote Foo is expecting to use whatever and shouldn't assume that it isn't null if it is a pointer. If you check that it's null and return or log the error or throw it then the person calling knows they never passed anything in for it. Saying that you -have- to use a constructor with a reference just to make it throw an error is a little asinine, it also severely limits the behavior of your object just because you're worried about someone not getting a compiler error because they flat out did not pass an object in.

Using frob's example it would be like calling a filestream a badly designed object because you allow people to create it without providing a file to open, and thus if they use it and it reports an error state then it would have been fixed by demanding they pass a reference. Obviously both popular libraries and even standard library objects do not adhere to always requiring a reference to be passed in for dependencies on each object.

#49 fir   Members   -  Reputation: -448

Like
0Likes
Like

Posted 06 September 2013 - 12:26 AM

The obvious way in C++ is to have:

 

class Ship

{

public:

void rotate(float angleRad); // THIS WILL ROTATE BOTH ANGLE AND UPDATE THE DIRECTION VECTOR SO THEY ARE ALWAYS ALIGNED

float getAngle() { return angle;}

Vector2 getDirection() {return direction;}

 

private:

Vector2 position;

Vector2 direction;

float angle;

};

 

The calculation is only done once, every time "rotate" is called, Ship is always valid. Welcome to 2013.. enjoy your 1960 language ;)

IMO, "angle" is not needed at all.

 

It seem to me that it not resolve the root core question i am talking about. even if one will have some clear syntax

that setting  angle = 10 will update the values of angle.nx

angle.ny to read - it is still unpleasant jump between setting

angle.nx angle.ny and using it. Such jump may be less or more problematic in practise I think but it seem to be real troublemaking.

 

Yet another somewhat related example (it is not easy to give

examples here for me because it is somewhat foggy topic for me):

 

I got a global shared Sleep value (in miliseconds) which is used in the winapi dispatch messages loop 

 

It is used yet in my some keyboard handlers module so I

could +-  it from keayboard (to incrase/decrease fps and corresponding cpu usage)

 

I can not clearly decide where this variable should belong

to my keyboard handler module or to dispatch messages loop (?) I placed it yet in the third place in globals as far as I remember.

 

This 3 places is already a mess for me - it become yet worse becouse I had a system of including and excluding some game modules to my system and in such module sometimes I like to overvrite such Sleep value for debug purposes locally, set it 5 in one module to 15 in other I am working on (other way I would must  find a initialisation and change it there so it it is yet worse)

 

As described above such global shared Sleep is an example of such global shared variable that brings a trouble (As I said before not all globals make such troble, some of them never do for me really and those troubles are more related to way of treating some globals not to all of them )

 

So how would someone resolve such Sleep trouble ?


Edited by fir, 06 September 2013 - 12:49 AM.


#50 kunos   Crossbones+   -  Reputation: 2203

Like
0Likes
Like

Posted 06 September 2013 - 01:26 AM

 

So how would someone resolve such Sleep trouble ?

 

 

with proper software design.

The very fact that you consider this an issue shows that there is no proper design into your code.. you just make everything visible to everything else and call it a day... give it some time and you'll see this will create a spaghetti mess impossible to maintain.

 

If a variable has to be visible to 3 subsystem it doesnt mean it has to be visible to the entire software.

From my point of view, the situation is straightforward: the var belongs to the subsystem using it (in your case, whoever is doing the game loop) and is "exposed" to a user interaction layer (in your case, a keyboard "manager").

 

There are real cases for "globals".. as somebody was already pointing out, user "vars" might be a good candidate for it. At the end it's all about making decisions and live with those. For example, in my current game I have a quake like "console".. that is exposed through loose functions instead of a class.. so it's a kind of a singleton. User "vars" are published to the console for user interaction accessing this global state through these functions. It is a convenient decision because it makes this particular process less tedious but it comes at the known price of coupling every class using the global console to it without a CLEAR dependency relationship expressed via constructor.

 

@Frob.. I think the misunderstanding here is due to the fact that we are probably looking at different parts of a game.

For GameObjects serializable classes what you say makes perfectly sense.. it's mostly data driven, designed to be built and sculptured at runtime. But "engine" classes that handles hardware resources do require a much clearer initialization and dependency rules.. you can't build this if you don't have that... DX11 is a perfect example of this.. you cannot have a View to a resource if you dont have a Resource! It wouldn't make any sense.

 

GameObjects are an exception to that because their point is to be able to express dynamic relationships, uncertain content and interactive manipulation (ie from game editors).. you need to build an empty GameObject because that's what you'll be doing while editing the game. but would you build a MeshRenderer component without a reference to some sort of render manager thingy? And then have your code checking "if (renderManager)" everywhere? If the answer is yes then sorry.. I don't care how many games you have worked with that approach.. but it doesnt make sense AT ALL.


Stefano Casillo
Lead Programmer
TWITTER: @KunosStefano
AssettoCorsa - netKar PRO - Kunos Simulazioni

#51 Satharis   Members   -  Reputation: 949

Like
0Likes
Like

Posted 06 September 2013 - 01:38 AM

So how would someone resolve such Sleep trouble ?

By making it part of the system that it is designed for.

Generally you want to make data as object local as you can, for instance if I had an enum I created that defined ordinal directions in a 2d game like north, east, south, etc. Quite a few systems might use that, in that case I might place it into a header file under a namespace that can be included by any parties interested in using that enum. For something like a sleep variable that isn't a constant you would probably want to make it belong to the thing that uses it the most, i.e. your window or engine class or whatever. Then just allow it to be accessed from other parts of code that need to determine what it is or modify it.

Using another example you may create an enum of substates for a state machine, well if only that state machine uses it and any other objects would -have- to include the state machine to ever be interested in using the enum then you could place it in the header file. It's a balance of trying to keep information local to where it is used while not mashing everything together in globals or giant include files. It's actually a bad idea to have a header file like "constants.hpp" or something as well due to the fact that everytime that file changes it will force recompilation in the other files.

#52 fir   Members   -  Reputation: -448

Like
0Likes
Like

Posted 06 September 2013 - 02:21 AM

For something like a sleep variable that isn't a constant you would probably want to make it belong to the thing that uses it the most, i.e. your window or engine class or whatever.

 

 

This one seem to be reasonable advice i think. Indeed probably removing it from the globals module (I put it there because I was tending to forgot where it can be so it is easy to find) and defining it into window module - and reference from all the others. This is some improvement.- But I am still not sure if this removes some troubles I eventually see still related

 

(such problems are related maybe to fact that 1-1 'linear' dependencies in code are clear, but when you have such 'accesible spots' you can have uncontrolled many-many 

dependencies and it brings some trouble

 

[but I am not sure if this is just about that, this is I think related not only to system of referencing but to some code flow architecture] 

 

- I "will be must" (Wiill had to ? future neccesity do not know how to write this in english  ) rethink it still more, to find, if there is no way to repairing it more, by some other elaborate design or something)


Edited by fir, 06 September 2013 - 02:51 AM.


#53 fir   Members   -  Reputation: -448

Like
0Likes
Like

Posted 06 September 2013 - 02:28 AM

(blank, sorry mistaken click)


Edited by fir, 06 September 2013 - 02:33 AM.


#54 Danicco   Members   -  Reputation: 449

Like
0Likes
Like

Posted 07 September 2013 - 11:47 AM

I'd like to thanks for the replies, I kept thinking of how could I change my code design and I think I got something a little better than before.

 

I didn't really like having static variables in my engine since the game class (exposed to other programmers) could access it as well and potentially break something.

I added a "resourceLoader" so all objects that wants to use the engine resource module must go through, and then it can manages itself and I don't need to have it global static for the code:

 

void Game::SomeFunction()
{
    Image* myImage = new Image();
    myImage->Load("assetName"); //I accessed the static resourceModule here
}

 

Now it is:

 

void Game::SomeFunction()
{
   //Since I'm already inside game, and game has access to engine
   Image* myImage = engineResources.Load<Image>("assetName");
   //If "assetName" hasn't been loaded yet, it'll call "Image->Load()" so I don't have all load functions inside my resource module
   //I want to make objects manage themselves so it's easier to handle
}

 

I still left the original constructor open so Game can build their own objects if needed (such as simple Quad, or loading something from outside the resource Module, though they'd need to manage it themselves).

 

So that's something less that I need to worry about outside access that could mess things up, thanks for the help!



#55 warnexus   Prime Members   -  Reputation: 1399

Like
0Likes
Like

Posted 08 September 2013 - 10:50 PM

Static variables can be used. But they must not be used with a lack of knowledge of what static is capable of doing to the results you expect not to have but in fact have gottern.

 

Suppose you made private static int life for all instances of the Monster class, what would happen to the monster when your ship shot a laser at it? Assuming the laser can kill it with one hit and also assuming there are more than one instance of the Monster class in the game winodw, both monsters will in fact be affected with the hit even though you only aim at that one monster.

 

Static variables has its benefits. Suppose when you want to access a method of a class without having creating the object of the class This acts as a convenience.

 

If you find yourself misusing static, this is a clear indication to re-evaluate your code design in terms of object-oriented design.

 

A good example of a Student class using non-static and static variables would be

 

 

public class Student

{

 

private static int unitsToGraduate = 120;

private int name;

private int age;

 

}


Edited by warnexus, 08 September 2013 - 10:53 PM.


#56 Sacaldur   Members   -  Reputation: 483

Like
0Likes
Like

Posted 09 September 2013 - 04:48 AM

Static variables has its benefits. Suppose when you want to access a method of a class without having creating the object of the class This acts as a convenience.

You want to do so when you have Code unrelated to any class or you're not able to use a non-static way (e. g. because your function does some mathematics and you're not able to extend the basic datatypes). If you're working object oriented you want to use methods and in most cases it's possible to do so. It requires a bit more time to get to a better solution and sometimes you're just using the first solution coming to you're mind.
 

A good example of a Student class using non-static and static variables would be

 

public class Student {
    
    private static int unitsToGraduate = 120;
    private int name;
    private int age;
    
    // [...]
    
}

... assuming there is only a single university or wathever the students are used by in your system. As soon as you have multiple universities with different unitsToGraduate values, you have to move the static variable into the university (regular member) and you should recognize: this value is a universities property.
That's why also this example is at last also not a good example for the usage of static variables.

In general speaking: You won't have to use static variables in most cases. And if you believe there is no other way without a static variable, there is probably still an other way. ;)

#57 warnexus   Prime Members   -  Reputation: 1399

Like
0Likes
Like

Posted 10 September 2013 - 09:02 AM


In general speaking: You won't have to use static variables in most cases. And if you believe there is no other way without a static variable, there is probably still an other way. ;)

 

Well you would actually have to use a static. Specifically, you would need to use a static method instead of the static variable to present encapsulation in your code.

 

 

 


As soon as you have multiple universities with different unitsToGraduate values, you have to move the static variable into the university (regular member) and you should recognize: this value is a universities property.

 

Would a programmer really do something like that? Given your scenario, I would actually make the unitsToGraduate an instance variable for each instance of University class.

 

The example I gave was demonstrated by a Stanford University professor.

 

I do agree, it depends on the situation.


Edited by warnexus, 10 September 2013 - 09:25 AM.


#58 Sacaldur   Members   -  Reputation: 483

Like
0Likes
Like

Posted 10 September 2013 - 03:38 PM

As soon as you have multiple universities with different unitsToGraduate values, you have to move the static variable into the university (regular member) and you should recognize: this value is a universities property.

 
Would a programmer really do something like that? Given your scenario, I would actually make the unitsToGraduate an instance variable for each instance of University class.

Aren't you saying the same thing I did?
public class University {
    private int unitsToGraduate;
    private List<Student> students;
    // no static
    
    [...]
}

public class Student {
    private String name;
    private int age;
    // no static
    
    [...]
}
 

The example I gave was demonstrated by a Stanford University professor.

I'm uncertain about the situation in other countries, but as far as I heard about you should not rely on everything IT professors are telling.




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS