> Now, why do I think of *this* as being defensive programming, whereas
> quoting hash keys feels like a superstition?
Because in this case you're providing logical closure.
Programming is about translating human assumptions into symbols a computer can manipulate. The more completely you lay out your assumptions, the better your code tends to be.
In this case, you want to process 10 items. That's the assumption you're translating into code. It's easy to complement that assumption by saying, "we don't want to process more than 10 items," and then we can break that down into the mutually exclusive statements: "we have processed less than 10 items," and "we have processed 10 items or more."
Those statements are useful because they cover every possibility. It's safe to assume that any runtime configuration will fit into one of those two categories, and that we can Do The Right Thing as long as we know which category we're in.
So you created a counter named $count, and used that to model the assumption "how many items we've processed." Doing so injects the new assumption, "$count always matches the number of items we've processed." There's no guarantee of that in $count itself, because $count is a scalar and can hold a wide range of values, including integers, strings, references to other variables, and floating-point values. Therefore, you have to write code to enforce the assumption, and your program is only as reliable as that code.
You chose to use $count++ inside a loop, and to increment $count every time you process an item. The code for processing and the increment have to stay welded together, or the assumption that $count matches the number of items processed will break. Nor can you touch <t>$count anywhere else in the program, or again, the underlying assumption will break.
Now, if you'd chosen $count == 10 as your halting condition, you'd add the assumption: "we will always be able to see the point where we leave the asumption 'we have processed less than 10 items' and move to the assumption 'we have processed 10 items or more'." Again, there's nothing in in $count that will guarantee such behavior, in fact, there's a lot that will happily break that assumption. So once again, you'd have to enforce that assumption with code.
Sure, $count++ meets those conditions (asssuming you started with an integer value, and that $count isn't touched anywhere else in the program), but there are plenty of ways to modify the code that will break what is admittedly a very brittle assumption.
Worst of all, propagating assumptions through your code like that creates silent dependencies that aren't actually documented by the code itself. They're things you have to remember, or that some other programmer will have to extrapolate from reading the code. If you decide to add a new way of processing items, you have to remember to increment $count as well, because those two concepts have to stay welded together. If a typo clobbers the value in $count, your program will behave in hard to predict ways because the code no longer matches an assumption that isn't explicitly stated anywhere.
Personally, I'd do this:
my @found = (); my $err = ''; for $c (@$Courses) { if (&is_good ($c)) { push @found, $c; } else { $err = "Problem: $c failed the test.\n"; } last if (($err) || (@found >= 10)); } for $c (@found) { print PageCourseInfo ($c); } print $err if ($err);
because taking the length of a list of found items matches the underlying assumption better. Its size will always matches the number of items we want to process, and will always be an integer greater than zero. And you'll note that I'm still using @found >= 10 instead of @found == 10, because that does the right thing for any value of scalar @found.
In reply to Re: Defensive Programming
by mstone
in thread Defensive Programming
by George_Sherston
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |