PDA

View Full Version : Why would I want to overload a method?


Banana
2008-06-03, 21:54
I understand that in some languages, overloading is supported. However, I'm not sure if I get how this is supposed to be a feature. There seems to be two main uses; to supply defaults for missing arguments then call one method that has the most arguments, or to do something very different based on numbers of argument.

Coming from Visual Basic, I don't understand the need to overload as I can just specify optional parameters and use IsMissing function to provide defaults for the optional parameters or have it act a bit differently so I get to keep all code in one place instead of having to looking at several methods with same names to figure which one I want.

Enlightenment will be much appreciated! :)

Kickaha
2008-06-03, 21:59
You shouldn't care which one you want, it should be determined solely by the arguments you send in... which should be determined by what data you have on hand.

If you have a plethora of possibilities that you can use, you probably are looking at an extremely over-engineered system. ;)

Brad
2008-06-03, 22:01
You shouldn't care which one you want, it should be determined solely by the arguments you send in... which should be determined by what data you have on hand.
++

Simpler and more to-the-point than the explanation I was about to share.

rollercoaster375
2008-06-03, 22:04
It's cleaner. Checking for optional parameters can get tedious, and overarching if statements in your functions get really annoying sometimes.

It's also very helpful for dealing with input parameters of different types... For example, a "to_string" function needs to be able to deal with both integers and floats.

Kickaha
2008-06-03, 22:11
++

Simpler and more to-the-point than the explanation I was about to share.

Danke. To expand on what rollercoaster said, the method name should convey the intent of what it is to accomplish. In the case of something like "toString", you could have any number of things you want to convert to a string representation.

You could have intToString(int), floatToString(float), etc, etc, and it would be explicit, but it would be fragile.

Assume you have a system where you have a bunch of calls of a type that you want to convert to a String for debugging or, heck, conversion for human readable output for other reasons. So in a few hundred places in your code, you have a call to myTypeToString(myType). Okay... now what if you need to change the type of the data you're working with?

Oops.

You can go in and change all the myTypeToString to myNewTypeToString, of course, but you need to check each one by hand. Ouch. Or you could convert myNewType to an instance of myType before sending to myTypeToString, but that's not much better.

Or you could have had the call by toString() in the first place, and let the compiler take care of it. :)

'toString' is a nice, compact statement of intent. What it's converting to the string is immaterial from the point of view of that statement. You should, optimally, be able to toss any old item at the method call and have it work. Overloading is how you partition the abstraction space into pieces suitable for implementation, and let the compiler do the grunt work based on the types of the arguments.

Banana
2008-06-03, 22:18
You shouldn't care which one you want, it should be determined solely by the arguments you send in... which should be determined by what data you have on hand.

Right, I guess I was thinking more in terms of keeping code concise and clean.

If you have a plethora of possibilities that you can use, you probably are looking at an extremely over-engineered system. ;)

That very well could have been the case with the code I was looking at- overload everything and even overload for overloaded method! :eek:

++

Simpler and more to-the-point than the explanation I was about to share.

Oh come on, I would so love to hear your side! :p

It's cleaner. Checking for optional parameters can get tedious, and overarching if statements in your functions get really annoying sometimes.

If there's several optional parameters, then Case statement would be more appropriate. Even so, why put codes in different places when I only need to find one routine and I have everything that pertains to it rather than trying to locate the right method and editing it and hope that it doesn't make other methods go haywire.

It's also very helpful for dealing with input parameters of different types... For example, a "to_string" function needs to be able to deal with both integers and floats.

Ooo, good one. This is definitely better than passing off a Variant data type- I despise this sort of type mainly because it's so fuzzy and meaningless and I have to figure out just what it's is before doing something with it.

'toString' is a nice, compact statement of intent. What it's converting to the string is immaterial from the point of view of that statement. You should, optimally, be able to toss any old item at the method call and have it work. Overloading is how you partition the abstraction space into pieces suitable for implementation, and let the compiler do the grunt work based on the types of the arguments.

Makes perfect sense.

Kickaha
2008-06-03, 22:21
If there's several optional parameters, then Case statement would be more appropriate. Even so, why put codes in different places when I only need to find one routine and I have everything that pertains to it rather than trying to locate the right method and editing it and hope that it doesn't make other methods go haywire.

By that reasoning, you should just have all your application code in one main function. :lol:

I'll take 8 appropriately refactored short methods over one long one, any day.

Banana
2008-06-03, 22:27
By that reasoning, you should just have all your application code in one main function. :lol:

Touché! :lol:

I'll take 8 appropriately refactored short methods over one long one, any day.

To be fair, when the overloaded methods basically call one method with most numbers of argument, it's not that bad because I only need to concern myself with that method, expect for data type conversion, which probably would be best left in the overloaded method, and even so, it can be then sent to the method if necessary to keep code in one place, figuratively speaking.

Banana
2008-06-04, 13:48
Okay, here's a bit more practical question.

Suppose I have a function that may or may not instantiate an object, based on arguments being passed. From what I see, it's more common for a overloaded function to supply the defaults for missing arguments then call the function with most arguments. Yet, if this involves a object, it will instantiate the object anyway, whether we actually need it or not.

Therefore, I'd prefer to use a If/Then block to short circuit the instantiation and thus save a bit of resources and make it a bit speedier, but also just because it's the right thing to do. Is there a reason not to do this, or maybe I don't know of a better approach to deal with not instantiating unwanted objects when called the final method?

Kickaha
2008-06-04, 14:13
:err: What?

Time for example code, bucko.

rollercoaster375
2008-06-04, 14:31
Sounds like poorly implemented overloading. If it can be done in a more efficient way in those instances, why wouldn't the overloaded function use that method?

The thing to keep in mind is that overloading is just one way to solve a problem. In some instances, it's really helpful. In other instances it's not. You, as the programmer, are the one that needs to distinguish between the two.

Banana
2008-06-04, 14:43
Here's an example of a method I modified but didn't overload:

public SshStream(string host, string username, string password, string pathname, int lport, string ipaddress, int rport, string hostkey)
{
this.m_host = host;
JSch jsch=new JSch();
m_session=jsch.getSession(username, host, 22);
if (pathname == null)
{
m_session.setPassword(password);
}
else
{
m_session.jsch.addIdentity(pathname,password);
}

m_session.setPortForwardingL(lport, ipaddress, rport);


Hashtable config=new Hashtable();
config.Add("StrictHostKeyChecking", "yes");
m_session.setConfig(config);
jsch.setKnownHosts(hostkey);
m_session.connect();
m_channel=(ChannelShell)m_session.openChannel("shell");

m_in = m_channel.getInputStream();
m_out = m_channel.getOutputStream();

m_channel.connect();
m_channel.setPtySize(80, 132, 1024, 768);

Prompt = "\n";
m_escapeCharPattern = "\\[[0-9;?]*[^0-9;]";
}

I could have created two methods, one without the argument for pathname, which would have made the connect a password-authentication rather than publickey-authentication. Keeping the principle of keeping code in one place, the method would call the overloaded method like this:

public SshStream(string host, string username, string password, int lport, string ipaddress, int rport, string hostkey)
{
/// Pass the pathname as empty string
SshStream(host, username, password, '', lport, ipaddress, rport, hostkey)
}

public SshStream(string host, string username, string password, string pathname, int lport, string ipaddress, int rport, string hostkey)
{
this.m_host = host;
JSch jsch=new JSch();
m_session=jsch.getSession(username, host, 22);
jsch.addidentify('')
...

This would have created a new object (HostKey which is created as part of the addidentify procedure) with no information in it which would just be a waste. I could then rewrite the first method to be copy of the second method but without that bolded line but that gives me two different methods to maintain (suppose I want to change something else in that process? Oops) so it all come back to using a if/then block.

I ask this to see if this is indeed the best way to approach this, or whether I don't know of a better way to do this.

I hope this makes sense...



Rollercoaster- I quite agree that it is up to *me* to decide; this is why I ask because I know that I may not know a better way to handle overloaded method which may have undesired result.

ShadowOfGed
2008-06-04, 15:16
I absolutely hate methods with that many parameters. Because, like you're seeing, having that many parameters means it's unlikely that someone will want to specify all of them. Alternatively, you could create a "configuration" type, something like SshStreamConfig, whose constructor sets all of the default values. Then a client can override the specific connection parameters they wish to set, and then you just pass a single SshStreamConfig into the SshStream() function.

Your mileage may vary. ;)

Banana
2008-06-04, 15:30
Gotcha. I had that feeling about having too many parameters, but it was just a quick'n'dirty as I was focused on understanding how it ticked and figured I'd go back and tidy it up.

However, I am not sure if I understand how a configuration type would not invoke the unwanted objects? I mean, when I get the constructor type I'd still have a bunch if/else to make sure I don't invoke the unneeded objects, no?

ShadowOfGed
2008-06-04, 15:39
However, I am not sure if I understand how a configuration type would not invoke the unwanted objects? I mean, when I get the constructor type I'd still have a bunch if/else to make sure I don't invoke the unneeded objects, no?

Invoke? Do you mean create/allocate/instantiate?

I assume this is Java? I don't know how much Java will optimize, but the attributes of the default config would probably be references to constant values (i.e. not duplicated for every new instance). You'd only allocate new objects when modifying the default configuration to something different. Yes, you'd have to allocate a new configuration object for each connection (or use one for multiple connections, if you're opening several), but the object allocation overhead is going to be negligible compared to the overhead of opening a TCP connection to a remote host and whatever initialization SSH does.

Banana
2008-06-04, 15:47
C#, with some Java ported. This is from SharpSSH library.

Looking at the code, I can see that if I pass a password, the library doesn't instantiate a object representing keys, which totally makes sense to me. Yet, if I wanted to provide a choice of password or key file, I have to make sure I short circuit the logic based on the parameters being passed so I don't needlessly instantiate the public key object when a password is used.

Did that clarify?

Kickaha
2008-06-04, 15:50
Yeah, and... wow, that's a fscked up implementation if that's the case.

"Let's see, which arguments do I need to send in to fine-tune the *implementation side effects*... I know, I'll look at the source code!"

Ye gods.

Banana
2008-06-04, 15:57
Well, the library was pretty big, and as I said, it was ported from Java, so there were C# wrapper classes which didn't expose what I needed for my project so I had to modify the class. The java source code was totally undocumented so I had to use the wrapper class to have any idea what it would do...

Obviously I've done something stupid. What would be better approach than looking at the source code?

Kickaha
2008-06-04, 16:01
No, I was commenting on the *NEED* to look at source code to figure out how to use the library. To me, that just screams "I didn't write this library to be easily used, I was lazy."

Why not SSHStream as a baseclass, but authentication-agnostic, and SSHStreamFromPassword and SSHStreamFromKeyFile as subclasses?

Makes it explicit which one you're instantiating, the logic is completely separated, and common behavior goes into the SSHStream base class, which either can be used in place of in other logic.

An alternative would have been to have a single class that you then have methods usePassword and useKeyFile for, but then you'd need an explicit open, and a matching close, which could be forgotten. This way you take advantage of instantiate-to-grab-resources/delete-to-release which is good.

ShadowOfGed's suggestion of using a config object is another suggestion that can be used - set up the config object with the authentication scheme along with the other bits and pieces, and send that one thing into SSHStream's constructor to be pulled apart. (Doesn't help on the logic separation though, and introduces some unnecessary fragility.) The subclassing approach lets anyone make new subclasses on authentication scheme without having to know anything about the innards of the other classes. SSHStreamFromKerberos, SSHStreamFromOuijaBoard, whatever.

Banana
2008-06-04, 16:05
Whew, and I was worried I commited one of 7 deadly sins. :p

As matter of fact, there seems to be a abstract class, SSHBase, which can be turned into SSHStream, SSHShell, SCP, SFTP. But they didn't do anything about implementing keys or other authentication methods, I'm afraid.

That would be even be better- I could just implement SSHStream with all the functions, then add the subclass to deal with the authenication (but I think I'm pretty much stuck with the Java implementation which still need me to instantiate the necessary objects to do so and so...)


Now I'm glad I asked! I learned something useful. :)

Kickaha
2008-06-04, 16:10
Heh. Sorry, personal pet peeve. If I have to go read the source code for a method to figure out how to *USE* the method, the developer has FAILED IMNSHO. The method should be as self-documenting as possible at the calling point, and the rest should be *in the documentation*. The idea that I have to go picking through somebody else's code line by line to figure out how to call it safely or properly is just asinine, and devs who force that on their users and colleagues should be shot on sight.

Not that I have an opinion or anything. ;)

Banana
2008-06-04, 16:13
Then stay away from this library.

It has 'foo' and 'bar' all over the place.


At least it works nominally, and does what I need it to do, and is free.... Most importantly, it's not your problem to pore over. :D

Kickaha
2008-06-04, 16:14
Must... control... Fist of Death...


Admittedly, I've been known to use foo, bar, iter and other throw away variable names for throw away places, such as loop iterators over a container, where the meaning is obvious.

But anywhere that meaning should be explicit? Capital punishment time.


Yeah, I have my own personal hell along these lines to deal with. (Goddamned firmware monkeys thinking they don't need to adhere to basic guidelines of software engineering because 'it's not software, it's firmware'... fsckers...)

Gargoyle
2008-06-04, 18:01
My advice would be to split this up into different methods. The following is gonna be Java/C# pseudo code:-

myStream = new SSHStream(user, host);
myStream.setPort(222); // Only need to call if not using 22!
myStream.setAuth(password); // Overload this method with setAuth(keyFile);
myStream.setTunnel(localPort, remoteIP, remotePort);
...
myStream.Connect();


As you can see, to make a basic connection you only need to call the constructor, the other methods are only needed for more complex connections.

Make sense?

Kickaha
2008-06-04, 18:50
Yeah, that was my first thought as well, except that I'm a big fan of managing resources by acquisition-on-construct/release-on-destroy when possible, and this is a prime candidate, AFAICT.

Generally, I use build-and-fill when it makes sense, but in this case I think the build-and-be-totally-done-immediately is a better fit.

Banana
2008-06-04, 20:42
Gargoyle- I can see the appeal, and believe that this is what Java portion of the library does in fact. That may explain the weird implementation- in the C# wrapper classes, we have subclassing, which I just described five posts above, but in ported Java, it's basically a giant class (actually two) with handfuls of supporting class, mainly representing files normally used to make up the SSH client, and they use methods like SetHostKey, SetConfig, etc. etc.

Kickaha, mind citing an example where you think Build-And-Fill would make more sense?

Kickaha
2008-06-04, 22:27
Sure - if you know what type of object you need independent from the time that you know what attribute values it will have. You can wait until you have all the data before creating it, or you can eagerly create it, and lazily fill it. This is useful in an asynchronous environment (such as a distributed/networked DB system), where you can instantiate the object, reserving whatever resources you need, then fill in the data as requests for the info are filled and come back.

This is used a lot in instances of Builder patterns - the builder is asked to create an instance of a particular type, and given hints on where to go get the data.

Banana
2008-06-05, 16:37
Okay, another question but this is a bit more about class.

In Visual Basic, I get to create a module which is just a container of various functions and subs. In C# this would be a static class.

However, it seems that if I declare a static class, I cannot use local variables for intermediate steps (e.g. typecasting). Eliminating the local variable would make for very awkward construction.

What is the reasoning behind this and am I approaching this wrong?

Kickaha
2008-06-05, 16:39
A static class cannot have instance members, by definition. Instance members are just that - ones that only exist when an object of that class is instantiated.

Static classes can't be instantiated, so... nope.

Wait... define what you mean by 'local variable'...

Banana
2008-06-05, 16:41
That's fine, but how would you then deal with procedures where you need more than one step to get the end result?

Surely not by calling a function like this:

public string foo(string bar)
{
return iter(_foo(_iter(bar))
}

Kickaha
2008-06-05, 16:45
Well, no, not unless you wanted an infinite recursion... :D

Instance variables != local variables.

Pseudo-code:

class A {
int foo; // <- instance variable
static int bar; // <- static variable

static void f(void) { // <- static method
int b; // <- local variable
};
};

A quick look at the C# ref, and static classes can contain only static methods and static variables. A static method can have a *local* variable, but it can't try and access an *instance* variable. (That's normal in static methods in any language.)

Banana
2008-06-05, 16:48
Aha, I didn't realize there was a difference between instance and local variables.

Will have to read this up. Thanks for noting the difference.


Edit: A quick look, and it seems that to have a static variable would mean that all objects from a class would share the same variables, hence the need for the distinction between local and instance.

It's also interesting because in VB, static variable means a variable that is retained even when the scope has left. Mindshift is fun! :)

Kickaha
2008-06-05, 16:51
No problem. I haven't ever used C#, so I'm not the world's expert on the language, but the only other option is them requiring you to use all static variables for local computation, which makes little sense.

Robo
2008-06-06, 07:09
I don't know why you would want to overload a method.

But I know you would want to overload on method.

method, the clean-obsessed purveyors of mostly-natural home care goods. (http://www.methodhome.com/)

Sorry, I could resist. It's rare that my two loves (computing and cleaning) meet. But don't you just love their minimalist packaging? :p

Banana
2008-06-06, 08:48
Sorry, I could resist.

I suppose you chose not to resist then? :p

Good pun, BTW. :D

Kickaha
2008-06-06, 10:05
Sorry, I could resist. It's rare that my two loves (computing and cleaning) meet. But don't you just love their minimalist packaging? :p

Wrong design arena. ;)

Robo
2008-06-06, 19:00
I suppose you chose not to resist then? :p

Good pun, BTW. :D

Whoops. And thank you. The pun was pretty much calling out to me ever since I saw the thread title.

The bottle for Lil' Bowl Blu is so cute that I feel bad about flushing its contents down the toilet. Is there something wrong with me? :lol: