2011-01-17 4 views
0

Ich habe eine Reihe von Feldern mit jedem Feld mit unterschiedlichen Satz von Validierungsregeln.Perl Unterroutine Referenz

Ich habe die Subroutinenreferenz für die Validierung eines Hash-Ref platziert.

Derzeit ist es in meinem Konstruktor, aber ich möchte es aus meinem Konstruktor in einem privaten Unter nehmen.

Ich habe es als getan unten

sub new { 
my $class = shift; 
my $self = {@_}; 

$class = (ref($class)) ? ref $class : $class; 
bless($self, $class); 

$self->{Validations} = { 
    Field1 => {name => sub{$self->checkField1(@_);},args => [qw(a b c)]} 
    Field2 => {name => sub{$self->checkField2(@_);},args => {key1, val1}} 
.. 
.. 
.. 
.. 
}; 

return $self; 
} 

Jetzt möchte ich alle diese Validierungsregeln aus meinem Konstruktor herausnehmen und wollen wie unten etwas, was zu tun, so dass ich eine bessere Kontrolle über meine Validierung haben basierte Regeln auf Typen Felder aus. (Sprich einige Regeln in einer Reihe von Bereichen gemeinsam sind, und ich kann durch Überschreiben der Werte von Feldern. für andere Regeln nur Regeln überschreiben)

bless($self, $class); 

    $self->{Validations} = $self->_getValidation($self->{type}); 

    return $self; 
} 
sub _getValidation{ 
    my ($self,$type) = @_; 
    my $validation = { 
    Field1 => {name => sub {$self->checkField1(@_);}, args => {key1 => val1}},}; 

    return $validation; 
} 

Aber ich bin immer Can't use string ("") as a subroutine ref while "strict refs" in use at... kann mir jemand sagen, Warum ist dieses Verhalten mit? Subref. Wenn ich meinen Namensschlüssel überprüfe, wird er null oder sub {DUMMY};

+0

Eine weitere Sache hier hinzufügen, ich habe es getan 'Field1 => {name => sub {$ self> checkField1 (@_);}' so muss ich irgendwann auch einzelne Felder validieren, und dachte daran, einzelne Feldregeln wie '$ obj-> checkField1 aufzurufen ('string', @ args); 'Wenn es einen besseren Weg gibt, bin ich bereit, meinen Ansatz zu ändern – awake416

+0

Lustiges Ding ist hier passiert, es funktioniert ohne irgendein Problem, eigentlich habe ich die Keys definiert, die Field1 starten und das erste erzeugte Feld war Field0: - (... Aber diese Frage steht noch offen für einen besseren Ansatz. – awake416

+0

Sind die Validierungen tatsächlich pro Objekt unterschiedlich, dass Sie sie in ein Attribut einfügen, oder machen Sie es nur, um '$ self' zu erfassen? Weil Sie nicht brauchen ... – hobbs

Antwort

5

Es sieht für mich aus, als ob Sie nahe Moose schlecht zu erfinden sind. Erwägen Sie die Verwendung von Moose statt etwas ähnliches zu bauen, aber weniger nützlich.

Die Fehlermeldung bedeutet, dass Sie eine Zeichenfolge an einer Stelle übergeben, an der Ihr Code einen Codeverweis erwartet. Holen Sie sich einen Stack-Trace, um herauszufinden, woher der Fehler kommt.

Sie können dies tun, indem Sie Carp :: Always verwenden, indem Sie den $SIG{__DIE__} Handler überschreiben, um einen Stack-Trace zu generieren, oder einen Carp::confess in Ihren Code einfügen.

Hier ist eine sigdie Lösung, bleiben diese in Ihrem Code, wo es vor Ihrer Modulinitialisierung ausgeführt wird:

$SIG{__DIE__} = sub { Carp::confess(@_) }; 

Möglicherweise müssen Sie es in einem BEGIN Block setzen.

Ich möchte Sie wirklich davon abhalten, diesen Ansatz zum Erstellen von Objekten zu verwenden. Sie segnen glücklich jeden zufälligen Mist, der dem Konstruktor als Teil Ihres Objekts übergeben wird! Sie greifen munter in Ihre Objekteinbauten ein. Feldgültigkeitsregeln * tun nicht gehören in den Konstruktor - sie gehören in das Attribut Mutatoren.

Wenn Sie ein DIY-Objekt verwenden müssen, aufzuräumen Ihre Praktiken:

# Here's a bunch of validators. 
# I set them up so that each attribute supports: 
# Multiple validators per attribute 
# Distinct error message per attribute 
my %VALIDATORS = (

    some_attribute => [ 
     [ sub { 'foo1' }, 'Foo 1 is bad thing' ], 
     [ sub { 'foo2' }, 'Foo 2 is bad thing' ], 
     [ sub { 'foo3' }, 'Foo 3 is bad thing' ], 
    ], 
    other_attribute => [ [ sub { 'bar' }, 'Bar is bad thing' ] ], 

); 


sub new { 
    my $class = shift; # Get the invocant 
    my %args = @_;  # Get named arguments 

    # Do NOT make this a clone method as well 

    my $self = {}; 
    bless $class, $self; 

    # Initialize the object; 
    for my $arg (keys %args) { 

     # Make sure we have a sane error message on a bad argument. 
     croak "Bogus argument $arg not allowed in $class\n" 
      unless $class->can($arg); 

     $self->$arg($args{$arg}); 
    } 

    return $self; 
} 

# Here's an example getter/setter method combined in one. 
# You may prefer to separate get and set behavior. 

sub some_attribute { 
    my $self = shift; 

    if(@_){ 
     my $val = shift; 

     # Do any validation for the field 
     $_->[0]->($val) or croak $_->[1] 
      for @{ $VALIDATORS{some_attribute} || [] }; 

     $self->{some_attribute} = $val; 
    } 

    return $self->{some_attribute}; 

} 

All dieser Code ist sehr schön, aber Sie haben Ihren Attributcode für jedes Attribut zu wiederholen. Dies bedeutet viel fehleranfälligen Standardcode. Sie können dieses Problem umgehen, indem Sie lernen, closures oder string eval zu verwenden, um Ihre Methoden dynamisch zu erstellen, oder Sie können eine der vielen Klassengenerierungsbibliotheken von Perl wie Class :: Accessor, Class :: Struct, Accessor :: Tiny usw. verwenden .

Oder Sie können lernen [Moose] [3]. Moose ist die neue (ish) Objektbibliothek, die Perl OOP-Praxis übernommen hat.Es bietet eine Reihe von leistungsfähigen Funktionen und reduziert dramatisch vorformulierten über klassische Perl OOP:

use Moose; 

type 'Foo' 
    => as 'Int' 
    => where { 
     $_ > 23 and $_ < 42 
    } 
    => message 'Monkeys flew out my butt'; 

has 'some_attribute' => (
    is => 'rw', 
    isa => 'Foo', 
); 
+0

danke daotoad, Ich sterbe, um Elch zu verwenden, aber ich kann es nicht in diesem Projekt wegen einiger beschwerlicher unvermeidbarer Beschränkungen verwenden :-(... – awake416

2

ich nicht gelesen haben alles, was man hatte, aber das fiel mir auf:

sub new { 
    my $class = shift; 
    my $self = {@_}; 

    $class = (ref($class)) ? ref $class : $class; 
    bless($self, $class); 

Normalerweise, wenn Sie eine erstellen neues Objekt, übergibt der Benutzer $self nicht als eines der Objekte. Das schaffst du.

Sie sehen in der Regel etwas wie folgt aus:

sub new { 
    my $class = shift; #Contains the class 
    my %params = @_;  #What other parameters used 

    my $self = {};  #You're creating the $self object as a reference to something 
    foreach my $param (keys (%params)) { 
     $self->{$param} = $params{$param}; 
    } 

    bless ($self, $class) #Class is provided. You don't have to check for it. 
    return $self #This is the object you created. 
} 

Nun $self keine Referenz auf einen Hash, wie in dem obigen Beispiel sein muss. Es könnte eine Referenz auf ein Array sein. Oder vielleicht zu einer Funktion. Aber es ist normalerweise eine Referenz. Der Hauptpunkt ist, dass der Benutzer $self nicht weitergibt, weil das von Ihrer new Subroutine erstellt wird.

Sie müssen auch nicht den Wert $class überprüfen, da dieser beim Aufruf des Unterprogramms new angegeben wird.

Wenn Sie Ihre Prüfung in einem privaten Klasse (eine ausgezeichnete Idee, übrigens) tun wollen, können Sie dies tun, nachdem die bless:

sub new { 
    my $class = shift; #Contains the class 
    my %params = @_;  #What other parameters used 

    my $self = {};  #You're creating the $self object as a reference to something 
    foreach my $param (keys (%params)) { 
     $self->{$param} = $params{$param}; 
    } 

    bless ($self, $class) #Class is provided. You don't have to check for it. 

    #Now you can run your verifications since you've blessed the object created 
    if (not $self->_validate_parameters()) { 
     croak qq(Invalid parameters passed in class $class); 
    } 
    return $self #This is the object you created. 
} 
+0

Dank für das Korrigieren von mir. Aber hier ist die Anforderung mehr über das Abrufen einer Reihe von Regeln basierend auf einem Parametertyp, sagen wir für ID-Typ Damen, ich möchte verschiedene Validierungsregeln und für Herren einen separaten Satz von Validierungsregeln verwenden. – awake416

Verwandte Themen