Multiple Functions vs Parameters

Started by
9 comments, last by Bob Janova 17 years, 7 months ago
The title might not make sense, but here's what I mean. Let's say you want to shout, talk or whisper in your game. Shout will display the text to many in a large area, talking will do it in a slightly smaller area, and whispering does it in an even smaller area. Would it be better to have 3 different function.. shout(){ //perform } talk(){ //perform } whisper(){ //perform } like so, or have 1 function with a parameter that is used to tell which command we're using and the parsing is done inside the function like.. talk(int type){ //if type = 1, whisper //if type = 2, talk //if type = 3, shout } Thanks
Advertisement
If you use multiple functions, then the if/else inside the unified function will not be there to eat up CPU cycles.

However, if you abstract the concept from human terms altogether, you could go with one function, no if/else:

void say(const float volume)
{


}
Yes, that is a good point, I guess I was using this as an example, but I think the method you suggested can be applied to much more than speech, thanks!
My bad, I edited the post to address your base concern.
Thanks, taby.Rating++; :D
Quote:Original post by taby
If you use multiple functions, then the if/else inside the unified function will not be there to eat up CPU cycles.

Don't worry about micro-optimizations when designing. Performance will most likely be the same since the paramter is likely to be a compile time constant, and then the compiler should remove the if-else itself.

Quote:However, if you abstract the concept from human terms altogether, you could go with one function, no if/else:

void say(const float volume)
{


}


We want to work at the highest high-level of abstraction realistically possible. Whisper, Talk and Shout is at a higher level of abstraction, but the generic function can also be useful so why not combine them:
void say(int volume){  // ...}void whisper(){  say( 1 );}void talk(){  say( 2 );}void shout(){  say( 4 );}

Hi CTar, I was only worried about it because it comes from experience.

I can see your point, but I don't believe that in this scenario that the parameter would ever be constant. If that was the case, then there wouldn't be any reason for this exercise in the first place.

Most importantly, since neither of our solutions involve if/else, I can assume that we are coming at the same solution from different angles is all. :)
Quote:Original post by CTar
Quote:Original post by taby
If you use multiple functions, then the if/else inside the unified function will not be there to eat up CPU cycles.

Don't worry about micro-optimizations when designing. Performance will most likely be the same since the paramter is likely to be a compile time constant, and then the compiler should remove the if-else itself.

If the function is inlined. If not, the if-else will always be there. Of course, there's a pretty good chance say(int) will require branching based on the provided value anyhow [eg: "SoAndSo whispers, 'Hello world!'"], or else will require more complicated calculations in order to get it to function properly without branching. Plus, practically by definition, communication is almost guarenteed to be a slow operation. So yeah, don't worry about micro-optimizations when designing.

I wonder about the usefulness of having say(int) as part of the public interface, but that depends on the actual usage.

CM
This is an excellent but subtle question about how to design code, and done poorly, this can be the source of many many headaches and bugs. The issue comes down to unreadable logic, and a programmer's desire to represent many different states with the same piece of code. The number of bugs in a piece of code is directly related to its complexity, which can be quantified as the number of possible states multiplied by the number of state transitions.

Trying to handle many overlapping states in one chunk of code is a very buggy way to do business. The main thing to realize is the really bad cases happen over a period of time, where certain parameters taken on more and more possible values and even combinations need to be handled specially.

Say, you just start off with Talk and Shout. They both play a sound, and Shout shakes the camera a little. You might write:

Talk( bool shouldShout ){     playSound();    if( shouldShout == true )        shakeCamera();}

okay, but now of course there should also be Whisper, which plays no sound but instead displays a subtitle...
Talk( int talkType ){    if( talkType == Whisper )        doSubtitle();    else        playSound();    if( talkType == Shout )        shakeCamera();}

Okay, starting to look gnarly, not so bad. Except now we add the option where the user can select subtitles to be shown for all sounds... And we add another type Scream that displays an italicized subtitle and shakes the camera.
Talk( int talkType ){    if( talkType == Scream )        doItalicizedSubtitle();    else if( talkType == Whisper || subtitlesOn )        doSubtitle();    if( talkType != Whisper )        playSound();    if( talkType == Scream || talkType == Shout )        shakeCamera();}

So now you're starting to get in a lot of trouble. As talkType grows in its number of possible values and interacts differently with the state of the game, this code is going to grow bugs real fast. Especially if there are other pieces of code that vary behavior depending on talkType, and especially if the person who adds another value to talkType isn't the one who wrote this function.

So save yourself a lot of headache and limit the scope of your functions sooner rather than later. However, if you have some hackery like "subtitlesOn" above, definitely pass that as a parameter rather than as global state. Varying behavior via globals is a whole separate hell which increases your implied state drastically.
This is a fairly subtle question. The basic distinction is: are the things you are trying to different types of the same action (in which case you should have one method which takes one or more parameters), or are they different things (in which case they should have one method each)?

In your little example, I'd make that one method with a volume parameter, or a type parameter, because whispering, talking and shouting are all forms of audible communication of words. But look out for the sort of ugly code Ajas is warning you about – if shouting is significantly different in how it acts from talking, it should be separated.

All this comes down to what your personal opinion of 'significantly different' is in the end. There is no right answer, but you should always be aware of the options open to you.

This topic is closed to new replies.

Advertisement