_roro: Programmierstil

Moin,

bisher baue ich immer Kontrollstrukturen auf, um Parameter zu handlen. Bei vielen möglichen Parametern wird diese Kontrollstruktur recht lang und unübersichtlich.

Eine andere Möglichkeit geht so, dass ich eingangs einen hash mit Referenzen auf Funktionen schreibe, was dann die Kontrollstruktur vereinfacht. Weiterer Vorteil: Ich habe alle erlaubten Parameter sofort im Blick.

Nachteil: Es führt dazu, das Funktionen in Funktionen aufgerufen werden, das ist auch nicht gerade schön.

Was gibt es noch für Möglichkeiten?

z.B.
#!/usr/bin/perl
###########################################################################
my %args = (
 'update' => &update,
 'insert' => &insert,
);
##########################################################################
use strict;

if(exists $args{$ARGV[0]}){
 $args{$ARGV[0]}();
}
else{
 print "unbekannter Parameter\n";
}

exit;
###########################################################################
sub update{
 # andere Funktionen aufrufen
 print "update $ARGV[1]\n";
 # andere Funktionen aufrufen
 return;
}
###########################################################################
sub insert{
 # andere Funktionen aufrufen
 print "insert $ARGV[1]\n";
 # andere Funktionen aufrufen
 return;
}

  1. Was gibt es noch für Möglichkeiten?

    Erklär doch mal wofür die Funktionsparameter zentral gehalten werden sollen. Und wo kommen die her? Aus dem HTTP-Request?

    Und was willst Du eigentlich genau wie kontrollieren mit Deinen Kontrollstrukturen?

    1. Was gibt es noch für Möglichkeiten?

      Erklär doch mal wofür die Funktionsparameter zentral gehalten werden sollen. Und wo kommen die her? Aus dem HTTP-Request?

      Wenns ein CGI ist ja.

      Und was willst Du eigentlich genau wie kontrollieren mit Deinen Kontrollstrukturen?

      1. ist der Parameter erlaubt
      2. was hat er für einen Value
      3. was mache ich damit

      z.B. mit der Kontrollstruktur bisher
      if(param()){
       if(param('insert')){
       # andere funktionen aufrufen
       # statements
       # andere funktionen aufrufen
       }
       elsif(param('update')){
       # statements
       # andere funktionen aufrufen
       # andere funktionen aufrufen
       }
       else{
        cgiError('unbekannter Parameter');
       }
      else{
       # script wurde ohne parameter aufgerufen
      }

      Meine neurlichen Überlegungen gehen hin die Kotrollstruktur zu vereinfachen (siehe POST weiter oben). Und, die erlaubten (Schlüsselparameter) auf einen Blick zu haben, so also (hier mal args genannt):

      my %args = (
       'update' => &update,
       'insert' => &insert,
      );

      Das vereinfacht die Kontrollstruktur dahingehend, dass nur noch ein einziges Statement für den Funktionsaufruf erforderlich ist:

      if(exists $args{$ARGV[0]}){
       $args{$ARGV[0]}();
      }
      else{
       print "unbekannter Parameter\n";
      }

      Führt aber dazu, dass weitere Funktionen aus den Unterfunktionen heraus aufgerufen werden müssen, was ich bisher stets vermieden habe. Hmm, wie macht Ihr denn das so?

      roro

      1. Wenns ein CGI ist ja.

        Führt aber dazu, dass weitere Funktionen aus den Unterfunktionen heraus aufgerufen werden müssen, was ich bisher stets vermieden habe. Hmm, wie macht Ihr denn das so?

        Entweder eine Eingangsüberprüfung auf Datentyp, Wertebereich und so oder der Parameter wird einfach an eine stored procedure weitergegeben, so dass der Datenserver entweder explizit oder implizit eine Prüfung vornimmt.

      2. Führt aber dazu, dass weitere Funktionen aus den Unterfunktionen heraus aufgerufen werden müssen, was ich bisher stets vermieden habe. Hmm, wie macht Ihr denn das so?

        Ich mach das in etwa so.

        Ich seh aber nicht das Problem eine Funktion aus einer Unterfunktion aufzurufen, letztlich ist bei mir die ganze Sache noch verschachtelter, da ich in die Kontrollstruktur auch noch die Information habe, welche Datei eingebunden werden soll. Das ganze ist ähnlich einem MVC Muster.

        Struppi.

        --
        Javascript ist toll (Perl auch!)
        1. moin Jens,

          Ich seh aber nicht das Problem eine Funktion aus einer Unterfunktion aufzurufen[..]

          Ok. Und wie machst Du das mit CGI-Parametern, übergibst Du die an die Unterfunktionen oder ermittelst Du die erst dort (CGI 'param')?

          roro

          1. Ich seh aber nicht das Problem eine Funktion aus einer Unterfunktion aufzurufen[..]

            Ok. Und wie machst Du das mit CGI-Parametern, übergibst Du die an die Unterfunktionen oder ermittelst Du die erst dort (CGI 'param')?

            In den Modulen per CGI::param()

            Struppi.

            --
            Javascript ist toll (Perl auch!)
            1. In den Modulen per CGI::param()

              Klaro, Module schreibe ich auch um eigene Funktionen auszulagern, übergebe bei hashes und arrays nur refs, ansonsten Literale.

              Mit CGI mache ich jedoch so, dass ich die Parameter übergebe, also nicht erst in den subs ermittele. Das hat den Vorteil, dass ein evntl. Wechsel zum OOP-Style nur an wenigen Stellen erfolgen muss (in der Kontrollstruktur, also von param() nach $cgi->param()).

              Quick & Dirty ist folgender Hack:

                
              my %in = map{$_, param($_)} param();  
                
              if(param()){  
               if($in{edit}){  
                print cgiHeader();  
                print htmlUp("Eintrag bearbeiten");  
                print linking();  
                buzzdir();  
                editRecord($in{edit});  
                print htmlDown();  
               }  
              # usw..  
              
              

              Razz Fix wird aus
              my %in = map{$_, param($_)} param();
              my %in = map{$_, $cgi->param($_)} $cgi->param();
              und schnappt sich die Parameters/Values in den globalen hash %in.

              roro

              1. Mit CGI mache ich jedoch so, dass ich die Parameter übergebe, also nicht erst in den subs ermittele. Das hat den Vorteil, dass ein evntl. Wechsel zum OOP-Style nur an wenigen Stellen erfolgen muss (in der Kontrollstruktur, also von param() nach $cgi->param()).

                Wie schon geschrieben ich benutze immer CGI::param(), also weder das ein noch das andere. Da ich das CGI Modul nicht als Objekt einsetze und so nur an einer einzigen Stelle einbinden muss.

                Razz Fix wird aus
                my %in = map{$_, param($_)} param();
                my %in = map{$_, $cgi->param($_)} $cgi->param();
                und schnappt sich die Parameters/Values in den globalen hash %in.

                Wobei ich keinen Sinn darin sehe, diese Daten nochmals vorzuhalten, da sie ja schon von dem Modul gespeichert wurden.
                Ich hole mir die Parameter da wo sie gebraucht werden.

                Struppi.

                --
                Javascript ist toll (Perl auch!)
                1. moin Jens,

                  ich versuche mal, meinen 'neuen' Stil mit Deinem zu verbinden. Das Ergebnis ist verblüffend, swu. Ich find's nicht schlecht und werde das mal überschlafen.

                  roro

                    
                  #!/usr/bin/perl  
                    
                  # Rolf Rost 22.03.2007  
                  ###########################################################################  
                  # erlaubte Schlüsselparameter zur Steuerung des CGIs  
                  # und die zugeordneten Funktionen  
                  my %allowedParams = (  
                   'insert' => \&insert,  
                   'update' => \&update,  
                  );  
                  ###########################################################################  
                  use strict;  
                  use CGI 'param';  
                    
                  # Kontrollstruktur bestimmt Ablauf des CGI  
                  # keine weitere Abfage nach Schlüsselparametern  
                  # Schlüsselparameter stehen auf [0] im Parameter-Array  
                  if(param()){  
                   if(exists $allowedParams{(param())[0]}){  
                    $allowedParams{(param())[0]}();  
                   }  
                   else{  
                    cgiError('Unbekannter Parameter');  
                   }  
                  }  
                  else{  
                   browse();  
                  }  
                    
                  exit;  
                  ###########################################################################  
                  sub insert{  
                   my $was = param('insert');  
                   my $id  = param('id');  
                   print "Content-type: text/html\n\n";  
                   print "insert $was zu id => $id\n";  
                   return;  
                  }  
                  ###########################################################################  
                  sub update{  
                   my $was = param('update');  
                   my $id  = param('id');  
                   print "Content-type: text/html\n\n";  
                   print "update $was zu id => $id\n";  
                   return;  
                  }  
                  ###########################################################################  
                  sub browse{  
                   print "Content-type: text/html\n\n";  
                   print qq(  
                    <a href='$ENV{SCRIPT_NAME}?insert=0815&amp;id=1'>Insert</a> <br>  
                    <a href='$ENV{SCRIPT_NAME}?update=1984&amp;id=2'>Update</a>  
                   );  
                   return;  
                  }  
                  ###########################################################################  
                  sub cgiError{  
                   my $errMesg = shift;  
                   print "Content-type: text/html\n\n";  
                   print "$errMesg";  
                   exit;  
                  }  
                  
                  
  2. Hallo,

    bisher baue ich immer Kontrollstrukturen auf, um Parameter zu handlen. Bei vielen möglichen Parametern wird diese Kontrollstruktur recht lang und unübersichtlich.

    Und vor allem kann so eine Kontrollstruktur verschachtelte Abhängigkeiten nicht ausdrücken. Oder wie Lolli schon schrieb: Datentypen. Deswegen nutze ich so etwas (meist als Hash oder nur als "param in sys.argv[1:]") bei wirklich simplen Parametern. Irgendwann landet man dann eh bei einem Option Parser, also kann man auch etwas aus der Standard-Bibliothek nehmen und anpassen, das das für einen erledigt. Unter Python wäre das z.B. optparse. Bei Perl kenne ich mich mangels Praxis nicht so aus, aber unter GetOpt::* gibt's bei CPAN eine Menge. Das Parsen von Parametern und Optionen (meist im GNU-Stil) ist unter den üblichen Skriptsprachen ein gelöstes Problem; es besteht kein Grund, sich künstlich das Leben zu komplizieren.

    Nachteil: Es führt dazu, das Funktionen in Funktionen aufgerufen werden, das ist auch nicht gerade schön.

    Wo ist denn da der Nachteil? Wenn man halbwegs Event, Callback oder OOP-orientiert arbeitet, landet man eh dabei. Kein Grund, in Panik zu verfallen.

    Tim

    1. Irgendwann landet man dann eh bei einem Option Parser, [...]

      "Man" landet ja nicht eh bei dem o.g. Parser.

      Aber erkläre doch mal old Lully die Logik hinter der o.g. Annahme.

      Meine Logik geht in etwa so:
      Entweder RDBMS-seitig oder für Berechnungszwecke innerhalb der serverseitigen Logik (hier Perl) werden bestimmte Einstellungen auf Parameterebene erwartet, im schlimmsten Fall sogar Abhängigkeiten bzgl. des Werts des Parameters (Bsp.: A muss num. und grösser B (auch num.;) sein).

      Da sollte man natürlich bei "internen" Berechnungen im Perl-Funktionsheader Parameterprüfungen vornehmen. Geht es ins RDBMS, dann darf das auch gerne ausschliesslich RDBMS-seitig erfolgen.

      Aber die Prüfungen sollten nicht doppelt erfolgen, oder? (Ausnahmefälle mal aussen vorgelassen, also wenn bspw. Daten noch übertragen werden müssen).

      1. Hallo,

        Meine Logik geht in etwa so:

        Du denkst zu kompliziert, zu weit und dadurch am Thema vorbei.

        Mein Ausgangspunkt waren einfach nur normale unixoide Kommandozeilenskripte, bei denen man vor dem Problem steht aus einem String (den der Optionen) in einen bestimmten Zustand eines Programmes kommt. Und da lautet die Antwort: Parser, der den String parst, auf Gültigkeit gegenüber vorgegebenen Möglichkeiten überprüft und dann basierend auf den Optionen „etwas tut“, sprich Variablen setzen und/oder Callbacks aufrufen.

        Tim

    2. Hallo Tim,

      [..] Bei Perl kenne ich mich mangels Praxis nicht so aus, aber unter GetOpt::* gibt's bei CPAN eine Menge.

      Jow: Getopt.pm als PerlModul. Aber mir gings hier nicht um das Parsen von Parametern oder Argumenten, sondern um das Handlen von Parametern oder Argumenten über eine Kontrollstruktur, die den Ablauf eines Scripts bestimmt.

      Unglücklicherweise habe ich das hier beim Posts meiner Beispiele anfangs ein bischen durcheinandergewürfelt, das lag daran, dass ich, bevor ich ein PERL-Script zum CGI mache, dieses ersteinmal auf der Kommandozeile teste. Insbesondere ein Script, wo es mir darum ging einen neuen Stil zu finden.

      roro