posted 22 years ago
Just a few comments. The may all seem like fiddlin'-small points, but I feel it's really important to get each step right before you move on, so please don't be offended. What I plan to do is first tighten up your existing code, then look at how adding new features might work to try and show some of what "maintainability" means.
Let's consider your "mutators":
First a few overall issues.
A "mutator" is a method which changes (mutates) the contents of an object. These don't. I'd call these something like "business methods".
The mutators as suggested by a previous poster are a reasonable choice.
I hope you have already spotted the "void" which shouldn't be there. These are methods which return a type Complex, not a type "void" or "Complex void" (which makes no sense).
I'm very wary of comments which say something that the code should say. Remember that most people who see one of these methods will be seing a call to it, not the definition, so a comment in the definition is of little use. Code that reads well and expresses its intention is much more valuable. How about removing the redundant comments and changing the method names to "add" and "subtract" ?
Now on to the more tricky stuff which some people may disagree with.
I hope you still have the freedom to change the public interface to this class, as I feel you have started down a dangerous road. Imagine someone comes to this code and doesn't know or can't quite remember exactly what these methods actually do. It's not to great a leap to mistakenly assume that the "add" method actually accumulates it's argument to the object being operated on. After all "c1.add(c2)" can easily be read as if it is like "c1 += c2". This seems like needless possible confusion if we can make the code clearer.
I suggest that these methods shouldn't be instance methods at all, but class methods. Consider:
I feel that this much more strongly expresses what you are actually doing. because it is "static", it cannot modify an object's contents, and an "add" method which takes two arguments and returns the sum is very straight forward to understand.
If you really need the old syntax as well, it's trivial to add it, but please think hard before doing it:
When we do this for subtract too, and add in some of the previous suggestions, here's what we get:
Before we move on to adding new features, let's take a look at the code, to make sure it looks OK, and check if it is as good as it can be. If we can see any duplication of code, we should remove it. "Once and Only Once" (from Extreme Programming) or "DRY :- Don't Repeat Yourself" (from the Pragmatic Programmers) should be our mantra.
I can still see some duplication: "realPart = real;" and "imaginaryPart = imaginary;" both appear twice. So let's call setRealPart and setImaginaryPart in the constructor:
"add" and "subtract" have lots of similar lines, so we need to do something there, too. I can think of two basic ways to approach this. One is to refactor the code to gather the common lines together somehow. The other is to see how many of those similar lines we can eliminate altogether. I don't like to loose information, but a variables named "temp" don't really carry much information anyway. I feel that the following is only a bit less readable, and it does remove a lot of the apparent duplication !
Although there is still some duplication lurking there, eliminating it might overcomplicate the problem, so lets stop there for a moment:
I reckon by now you are beginning to wonder if all of this is getting anywhere. Sure, the methods are a bit shorter, but there's more of them. Now's the time to add some new functionality and see how easy it is.
Let's say we liked the idea of a way of "accumulating" adds in the same object ( like += ) when we thought about it earlier. Could come in handy if all that object creation and garbage collection does become a problem. Doing it the "old" way might have looked something like:
With all the new tricks we have learned and written for ourselves now, we might start with something like:
But we've also learned to look at the whole code and look for duplication, so we don't stop there. I can't help noticing that we now sum the reals and the imaginaries in both "add" and "increaseBy". Surely we can eliminate that. Making "increaseBy" call "add" doesn't help cut out the object creation, but we can refactor "add" to use "increaseBy"!
Interesting. Still looks a bit clunky, though, so I'd add a new constructor:
then we can write add as:
That seems quite neat. And notice how there was a lot less refactoring needed after that change, because the class was in much better "shape" to start with.
Now imagine someone also needs the "inverse" of a complex number (-re,-im). Can we fix it? Yes we can!
That was easy, wasn't it!. But, before we move on, we must look at the whole thing again. Think this is too small a change to make a difference? Think again! A subtract is defined as just the addition of an inverse, so we can reduce the subtract code to:
And there goes that niggling similarity between add/increaseBy and subtract.
So a final look at where the code is now:
Whew. That was more than I intended writing! Now it's over to you. Can you see any more ways to squeeze it? I can spot a few minor things I might change, but the main thing to learn is how to study your own code and make it the best you can.
I hope this helps someone.
Another day, maybe ask about how writing little automatic tests as you go along can help prevent screwups when tinkering like this...
[ August 20, 2002: Message edited by: Frank Carver ]