2010-10-27 8 views
6

Es gibt so etwas wie Statusänderungen in unserer App Logik überprüfen.repräsentieren zulässige Statusübergänge Graph in Perl

Derzeit hässlich Prüfung gehandhabt wird wollen if-Anweisung

ich es durch bergangsmatrix ersetzen: kann

my %allowed_status_changes = (
    1 => (2,5),  
    2 => (1,2,3,4,5), 
    3 => (4,2),  
    4 => (3,2),  
    5 =>(),   
); 
my $is_allowed_transition = 
    $submitted_status ~~ $allowed_status_changes {$original_status}; 

if ($prerequestsites && !$is_allowed_transition) { 
    return; 
} 

bestimmte Übergänge nur auf zusätzliche Bedingung erlaubt, daher werde ich etwas brauchen wie

2 => ( 
    (target => 1) 
    (target => 2, condition => $some_condition) 
    (target => (3,4), condition => $other_condition), 
    (target => 5) 
), 

(meiner Meinung nach ist es zu lang)

Welche Struktur würden Sie in dieser Situation verwenden, wenn Sie sich auf Lesbarkeit und Wartbarkeit konzentrieren sollten?

Wie Sie es analysieren, um zu überprüfen, ob der Übergang zulässig ist?

Antwort

2

Wenn die Bedingungen sehr vorherrschend sind (z. B. fast jeder erlaubte Übergang hat sie), dann ist Ihre letzte Struktur völlig in Ordnung, außer Ihrem Syntaxfehler beim Darstellen eines Hashref mit "()" anstelle von "{}".

Wenn die Bedingungen selten sind, würde ich vorschlagen, mit # 1 zu gehen, ergänzt durch optionale Konstrukte ähnlich wie Ihre # 2.

Bitte beachten Sie, dass die Lesbarkeit des Prüfcodes IMHO sehr klar, aber voluminös und nicht sehr idiomatisch ist.

OTOH, Wartbarkeit der Matrix ist hoch - Sie haben knappe, aber lesbare Syntax von # 1, wo keine Bedingungen benötigt werden und eine klare, aber längere Syntax für Bedingungen, die für viele Bedingungen flexibel genug ist, wie Ihre # 2.

my %allowed_status_changes = (
    1 => [2,5], 
    2 => [1,5,{targets=>[2], conditions=>[$some_condition]} 
      ,{targets=>[3,4], conditions=>[$other_condition, $more_cond]}] 
    3 => [4,2],  
    4 => [3,2], 
    5 => [],   
); 

sub is_allowed_transition { 
    my ($submitted_status, $original_status) = @_; 
    foreach my $alowed_status (@$allowed_status_changes{$original_status}) { 
     return 1 if !ref $alowed_status && $alowed_status == $submitted_status; 
     if (ref $alowed_status) { 
      foreach my $target (@$alowed_status{targets}) { 
       foreach my $condition (@$alowed_status{conditions}) { 
        return 1 if check_condition($submitted_status 
               , $original_status, $condition);  
       } 
      } 
     } 
    } 
    return 0; 
} 

if ($prerequestsites 
    && !$is_allowed_transition($submitted_status, $original_status)) { 
    return; 
} 
1

Obwohl ich mit DVK zum größten Teil zustimmen, muss ich sagen, wenn Sie Eintauchen in Arrays von Arrays von Hashes starten, werden Sie einen Code Komplexitätsgrad erreicht, die schwer ohne viel Spinnen halten von Köpfe und Käfer.

An diesem Punkt würde ich wahrscheinlich nach einem Objekt und einer Klasse für ein bisschen syntaktischen Zucker greifen.

my $transitions = TransitionGraph->new(); 
$transition->add(1, { targets => [ 2, 5 ] }); 
$transition->add(2, { targets => [ 1, 5 ] }); 
$transition->add(2, { targets => [ 2 ], conditions => [ $some_condition ] }); 
$transition->add(2, { targets => [ 3, 4 ], conditions => [ $other_condition, $more_cond ]}); 
$transition->add(3, { targets => [4,2] }); 
$transition->add(4, { targets => [3,2] }); 
$transition->add(5, { targets => [] }); 

if($transition->allowed(1 , 3)){ 

} 

Klasse Implementierung ist dem Benutzer überlassen, aber ich würde Elch verwenden.

Der Hauptvorteil davon ist, dass Sie die Funktionsweise des Zustandsdiagramms kapseln, sodass Sie es einfach verwenden können und sich darüber Gedanken machen müssen, wie das Diagramm unabhängig von seinem Verwendungszweck funktioniert.

nb. In der oben vorgeschlagenen API erstellt add() einen neuen Datensatz, falls noch keiner existiert, und aktualisiert diesen Datensatz, falls er existiert. Dies erwies sich als einfacher als "Upgrade" -Methoden oder "Holen Sie sich dieses Element und dann ändern Sie es" -Techniken.

Intern könnte es dies tun, oder so ähnlich:

sub add { 
    my ($self , $input_state, $rules) = @_; 
    my $state; 
    if ($self->has_state($input_state)) { 
     $state = $self->get_state($input_state); 
    } else { 
     $state = TransitionGraphState->new(source_id => $input_state); 
     $self->add_state($input_state, $state); 
    } 
    my $targets = delete $rules{targets}; 
    for my $target (@$targets) { 
     $state->add_target($target, $rules); 
    } 
    return $self; 
} 

sub allowed { 
    my ($self, $from, $to) = @_; 

    if (not $self->has_state($from)){ 
     croak "NO source state $from in transition graph"; 
    } 
    my $state = $self->get_state($from); 
    return $state->allowed_to($to); 
} 

Dies hat auch den kühlen Perk nicht einen bestimmten Code festgelegt erfordern auf den Unterknoten zu arbeiten, können Sie separate Instanzen erstellen, mit ihr eigenes Verhalten für den Fall, dass Sie möchten, dass ein Quellzustand anders behandelt wird.

$transition->add_state(2, $some_other_class_wich_works_like_transitiongraphstate); 

Ich hoffe, dies ist hilfreich =).

+1

nr. Ich habe diesen wahnsinnigen Zwang, alles zu abstrahieren. Ich hätte eine Moose :: Role für TransitionStateGraphs, und der interne Status-Hash würde mit moose-eigenen Merkmalen gemacht werden. Obwohl ich es so sehr mag, dass der Zustand bisher noch nicht erlaubt war, musste ich noch keine Schleife machen =) –

1

Es ist nichts falsch mit der zweiten Form. Sie werden nicht in der Lage sein, die Tatsache zu umgehen, dass Sie die Zustandsmaschine irgendwo kodieren müssen. In der Tat, ich denke, die ganze Maschine an einem Ort so codiert zu haben, ist viel einfacher zu verstehen, dass etwas mit zu vielen Ebenen der Abstraktion, wo man an n verschiedenen Orten suchen muss, um den Fluss der Maschine zu verstehen.

Verwandte Themen