perldesign-patternsperl-moduleperl-data-structures

Is there a way to replace an if-elsif-else in Perl with something better?


I want to build a bunch of Perl subrotines that all have the same template if elsif elsif else that takes a decision based on a factor variable. Here's an example of subroutine template:

sub get_age{

  my $factor=shift;

  if    ($factor == 1 ){ print "do something" }
  elsif ($factor == 2 ){ print "do somthing2" }
  elsif ($factor == 3 ){ print "do somthing3" }
  elsif ($factor == 4 ){ print "do somthing4" }
  else                 { print "error"        }
  }

I am wondering if there some design pattern on Perl to replace the if else condition with more elegant solution which easy to maintain in the future specifically if I need to change some of the conditions or delete some of it?


Solution

  • A couple of people have mentioned a dispatch table. There are two things and it's nice to keep them apart sometimes. There's the list of possible things that could happen, and the thing that makes them happen. If you couple the two, you're stuck with your solution. If you keep them separate, you have more flexibility later.

    The dispatch table specifies the behavior as data instead of program structure. Here's two different ways to do it. With your example you have integers and something like that might use an array to store things. The hash example is the same idea but looks up the behavior slightly differently.

    Also notice that I factor out the print. When you have repeated code like that, try to move the repeated stuff up a level.

    use v5.10;
    
    foreach my $factor ( map { int rand 5 } 0 .. 9 ) {
        say get_age_array( $factor );
        }
    
    my @animals = qw( cat dog bird frog );
    foreach my $factor ( map { $animals[ rand @animals ] } 0 .. 9 ) {
        say get_age_hash( $factor );
        }
    
    sub get_age_array {
        my $factor = shift;
    
        state $dispatch = [
            sub { 'Nothing!' }, # index 0
            sub { "Calling 1" },
            sub { 1 + 1 },
            sub { "Called 3" },
            sub { time },
            ];
    
        return unless int $factor <= $#$dispatch;
    
        $dispatch->[$factor]->();   
        }
    
    
    sub get_age_hash {
        my $factor = shift;
    
        state $dispatch = {
            'cat'  => sub { "Called cat" },
            'dog'  => sub { "Calling 1"  },
            'bird' => sub { "Calling 2, with extra" },
            };
    
        return unless exists $dispatch->{$factor};
    
        $dispatch->{$factor}->();   
        }