Making cosmetic changes to code is almost the essence of refactoring. According to Fowler (my emphasis):
Obviously 'easier to understand' is a subjective term (people can argue forever about Inline Temp vs Introduce Explaining Variable) - so take all the 'increases clarity' that follow as 'increases clarity for me'.
- Since we're not looking at the end of the line we don't need the chomp at all ...
- You're quite correct and this only applies if each line has more than two fields. I should have made that assumption explicit in the code or left the chomp in.
Finally bug-fixes gone wrong. I said:
We don't need the close - the filehandle will automatically be closed when it falls out of scope.
And you said.
that's terrible advice. The filehandle should absolutely be closed. It's a global (package) variable and probably won't go out of scope until the program ends. Leaving it open is an undocumented and probably undesired side-effect.
Quite correct. I'm stupid. It was late. Foolish Adrian.
It's instructive that I made this error because of a classic mistake. Let's look at the original code again:
sub get_schoolname( $ )
{
my $id = $_[0];
my $schoolname;
#get groupname from current database
open(SCHOOLDATA,"school_data.pdt") or die "Can't open school data
+file: $!";
while ( <SCHOOLDATA> )
{
chomp($_);
@split_record = split( /\|/ , $_ );
print $split_record[0];
if( $split_record[0] eq $id )
{
$schoolname = $split_record[1];
}
}
close( SCHOOLDATA );
return( $schoolname );
};
Now, when I looked at this I actually did notice that it stomped all over the global SCHOOLDATA :-)
What I should have done at this point is write a failing test.
However, due to a little too much hubris some variant of "The code is so simple! Who needs a test to demonstrate the bug" went through my head. Bad Adrian.
Later on I coding an extra close into the loop so that the file handle would be closed after the early exit in my version. As soon as I'd finished I thought - "Well that's pointless. Once you've localised SCHOOLDATA it will be closed when it falls out of scope" and removed both close statements.
I then wrote up my nice little decision to remove the close - and promptly forgot to go add the local that would make it valid!
So, instead of removing one bug I added a new one. Very bad Adrian.
Now, if I'd been a good little developer and written some failing tests:
use Test::More tests => 4;
{
local *SCHOOLDATA;
open(SCHOOLDATA, "school_data.pdt") or die;
is(get_schoolname(2), 'bunny werks', 'existing id found');
is(tell(*SCHOOLDATA{IO}), 0, 'filehandle still open');
};
{
local *SCHOOLDATA;
open(SCHOOLDATA, "school_data.pdt") or die;
is(get_schoolname(-1), undef, 'bad id rejected');
is(tell(*SCHOOLDATA{IO}), 0, 'filehandle still open');
};
I wouldn't have humiliated myself in front of my peers like this :-)
So the moral of the story is:
- Always write a failing test when you find a bug
- Don't put your bug-fixing and refactoring hats on at the same time
By the way, IMHO, his variable names were just fine for the task at hand. I would have chosen them differently and probably closer to the names you picked but that's largely a matter of style. Style is something we all develop for ourselves. It takes time and we find ourselves making niggling little decisions like whether to use $id_to_fetch or $fetch_id as you seem to have struggled with. Personally, I would have stuck with $fetch_id because I think prepositions in variable names are ugly and cumbersome but it's still a style decision.
Didn't have to struggle with the name change. Took longer to type than it did to think about.
While I agree that it is a style decision I think the fact that both of us would have chosen something other than '$id' is a pointer to the fact that the original choice wasn't as clear as it could be. Not wrong - just not as descriptive as it could be.
Rewriting code just because you can is rarely a good idea. You should always have a good reason for rewriting something.
For me increasing the clarity of the code (for some definition of clarity) is a good enough reason.
Even with good reason, you should be careful to keep the functionality equivalent unless you're sure it doesn't work.
Quite correct. Fouled up with the close & chomp changes.
In other words, if it ain't broke...
I tend to disagree. This approach can turn the code base of a large project into a Big Ball Of Mud in a surprisingly small period of time. Over the years I'm spending more and more time Refactoring Mercilessly and finding it works very well.
<Adrian wanders off to write out 'always write a failing test for a bug' 1000 times>