Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!swrinde!zaphod.mps.ohio-state.edu!cis.ohio-state.edu!pacific.mps.ohio-state.edu!linac!att!pacbell.com!pacbell!osc!jgk From: jgk@osc.COM (Joe Keane) Newsgroups: comp.object Subject: Re: Functions without side effects (was Old confusion) Summary: Why be confusing? Keywords: method name Message-ID: <4907@osc.COM> Date: 19 Jun 91 06:09:27 GMT References: <130242@tut.cis.ohio-state.edu> <4888@osc.COM> <72893@microsoft.UUCP> <4116@ssc-bee.ssc-vax.UUCP> Reply-To: Joe Keane Organization: Versant Object Technology, Menlo Park, CA Lines: 83 X-Moon-Phase: at the First Quarter Not surprisingly, something i wrote touched off a bunch of replies. So now i'm going to write some more on method names. Actually, i agree completely with Jim Adcock's main point, that redundant information should not be included in a method name. However, i'd say that it's hardly a minor point whether a method changes the object it's run on or merely returns some value based on it. In fact, when listing methods the most important distinction i make is between accessors and modifiers, or whatever you call them in your language. My rule on method names is simple. Methods with the same name should do the same thing. Really this is just common sense, but it can't hurt to make it explicit. Exactly what `the same thing' means is left open, but i think reasonable people will generally agree on whether two operations are the same. Of course, the methods don't have to perform exactly the same operations inside, and in terms of implementation they may be radically different. But at a conceptual level they should perform the same operation on different things. Here is an example of overloading: print(char); // print a character print(int); // print an integer print(const char*); // print a string print(const Widget&); // print a widget I think most people would agree that this is a proper and desirable use of overloading. Looking at the descriptions, which are actually pretty much redundant, you can see that these methods perform the same conceptual operation, and in fact the verb is the same as the method name. Here is another example of overloading: print(char); // enable Kanji printing print(int); // delete printing database print(const char*); // set printer device name print(const Widget&); // make a printer widget I think everyone would agree that this is a terrible use of overloading, and also that this is a silly example. But when you start putting multiple operations on a single name, you're venturing into this territory. I agree that in most cases a user can figure out what is meant, but that's not the point. Why make your methods confusing when they can be clear? A good convention to follow is that method names should be predicates, in the grammatical sense. This means that the first word should be a verb which desribes the conceptual operation, and then this is possibly followed by some nouns which clarify what the operation is to be performed on. Now it turns out that in many cases the verb will be a simple one, like `get', `set', `make', `add', `remove', or `replace'. In fact, i'd say that most methods are one of the preceding operations. This doesn't mean that the verb is not needed, it just means that the conceptual operation is a simple one. If the arguments are classes, you may not need any clarifying nouns. But in many cases the arguments are `int' or `const char*', and here it may not be immediately clear just what that integer is supposed to be. Furthermore, you may want to add another operation whose argument is the same type, although it means a different thing. One poster mentioned the possibility of a single method which returns a reference. I have one piece of advice on this: don't do it. If you do this enough i guarantee that eventually you will get screwed. I'm tempted to just leave this as a comment from experience, but i guess i should elaborate. First of all, references last forever. You never know when a user is going to decide to change the thing you passed him a reference to. Second, you may decide to change the internal implementation, so that the thing which you returned a reference to no longer exists. Then what do you do? You can't create a fake one, because you don't know when to get rid of it. Third, you may decide you want to do something when the object is changed, and this form doesn't allow you to distinguish when someone is just looking or is really going to change the attribute. I'd like to point out that all of the preceding comments apply to writing classes that other people are actually going to use. On the other hand, if you're writing a class for internal use, i suppose you can pretty much do what you want, as long as you can figure it out. However, i would like to remind people that even something you wrote may look a little unfamiliar after a couple years. -- Joe Keane, professional C programmer jgk@osc.com (...!uunet!stratus!osc!jgk)