views:

199

answers:

3

How do I separate out exception handling / error handling from business logic? I'm writing code in Perl, and the error/exception handling and business logic makes it very difficult to understand code while reviewing.

How can I refactor my code to make it more readable yet have error handling. Also note that I do not use try catch or anything like that.

One of our senior programmers suggested that we re open OS standard error and write everything there and we can catch it by the caller.

Edit : here is how I do the error handling . I have many Perl modules.. so check2.pm

package check2;
sub printData {
      print STDERR "Error Message from sub routine \n";
    }
    1;

and i use it like so in my Perl script , check.pl

In my Perl script

#!/usr/bin/perl
use LoadModules;
use strict;
use warnings;

load check2;

my $stderrholder;
local *SAVEERR;

# First, save existing STDERR
open SAVEERR, ">&STDERR" or print "not able to open";
close STDERR;

# Open the STDERR to a variable
open STDERR, ">", \$stderrholder or die "Failed to reopen STDERR $!\n";

#Start of processing

# Now print something to STDERR, redirected to $ stderrholder
print STDERR " Error Message from Main script \n";

check2::printData();

#End of processing

# Now close and restore STDERR to original condition.
close STDERR;
#open STDERR, ">&SAVEERR";

# Now check if there were any processing errors.
if(length($stderrholder)) {
  print "\nProcessing errors\n" ;
if(length($stderrholder)) {
  print "\nProcessing errors\n" ;
  print $stderrholder;
} else {
  print "\nNo Processing errors\n" ;
}

Would really appreciate if someone could help me point out errors in this.

A: 

Good question, but i think even though it makes the code more complicated, you should not remove error handling from business logic because there are often cases where business logic will need to know about the error.

I reckon you might want to re-factor your business logic so that the error handling is wrapped around the business logic. That might work, but i don't think its possible or even a good idea to separate error handling all the time.

I reckon the answer to your question heavily depends on the type of application and the complexity of the business logic.

Andrew Keith
A: 

Makes it very difficult how? Can you give an example of what is difficult?

You can make fairly readable code in perl while handling errors. For example, open(my $filehandle, '>', $filename) or die "Failed to open $filename" is an understandable english sentence. Also, you can use autodie to enable automatic catching of common errors.

One of our senior programmers suggested that we re open OS standard error and write everything there and we can catch it by the caller.

It's better than nothing I suppose, but don't stop there. Wrapping the entire code in a while(1) loop and a giant try/catch is not going to let you do anything sophisticated, plus it's one huge code smell.

Finally:

note that i do not use try catch or anything like that.

Why not?

Ether
Why not?One of our senior programmers suggested that we re open OS standard error and write everything there and we can catch it by the caller.infact its already in the code..I did suggest to use try catch..but they did not listen..as i m not a senior..
someguy
"open OS standard error and write everything there and we can catch it by the caller" makes no sense.
jrockway
I read it as "..we re-open stderr and write everything there" - although I don't see why it would be closed - printing to stderr (or any error log) is so basic I'm scared if they're not even doing that.
Ether
Edited the question for more clarity..
someguy
+3  A: 

Error-causing code

 sub whatever { 
    die "OH NOES" if an_error($occurred);
 }

Your main program:

 use Try::Tiny; # essential
 my $logger = anything_you_want;
 try {
     whatever;
 }
 catch {
     $logger->error("Error: $_");
 };

Throw exceptions when anything goes wrong. Handle exceptions where ever it makes sense; usually at the top level.

Sometimes you can "fix" an exception; example: connect to a failover server if the main server is unavailable. In that case, handle the failover somewhere higher-level than the top-level application, but somewhere closer than in the "connect" function:

sub connect {
    die "Error connecting: ..." if ...;
}

sub make_connection {
   my $connection = try { connect($main_server) };
   $connection ||= try { connect($backup_server) };
   die "Couldn't connect to either server" unless $connection;
   return $connection;
}

You would then handle "Couldn't connect to either server" in your top-level code, instead of each individual connection error.

Finally, you could also use the Error monad. This lets you return failure codes, but it ensures that no code is executed after the failure. (This approach works better for asynchronous event-based code... but most Perl programmers don't like monads and instead try to use exceptions for everything. That's fine, though... exceptions are a great way to handle errors.)

jrockway