Pattern per la convalida di regole con firme diverse

1

Ho una classe incaricata di rispondere a un evento di input e forse di attivare un altro evento. Per decidere, ha diverse regole da controllare.

Sto provando a scappare da una classe simile alla seguente:

public class Translator {
    public void maybeTranslate() {
        if (!condition1 || !condition2 || !condition3) {
            return;
        }

        // All rules have passed
        translate();
    }
}

Mi piacerebbe che questa classe sapesse che ha delle regole da passare, ma non conosce le implementazioni delle regole. Il mio tentativo era di trasformarlo in questo modo:

public class Translator {
    private Rule[] rules;

    public Translator (Rule[] rules) { this.rules = rules; }

    public void maybeTranslate() {
        for (Rule rule : rules) {
            if (!rule.passes()) {
                return;
            }
        }

        translate();
    }
}

dove Rule ha questa interfaccia:

public interface Rule {
    boolean passes();
}

Ecco il mio problema: ora ho una nuova regola che accetta un parametro. Il suo metodo passes richiederebbe un parametro String . Il mio fondamento è sbagliato con questo nuovo requisito? Non vedo come posso adattare questo modello per contenere regole con vari parametri.

Questo parametro indica la fonte dell'evento di input. Posso modificare maybeTranslate per ricevere questo valore da chiunque lo chiami e cambiare l'interfaccia Rule in boolean passes(String str) . Solo ora, la maggior parte delle mie regole ha un parametro che non gli interessa.

Questo è nel contesto di un'app per Android, dove Translator verrà istanziato su diversi frammenti.

UPDATE: alcune modifiche dalla mia domanda originale.

Questo Translator verrà utilizzato su più schermi. C'è solo una schermata alla volta e ogni schermata può avere un diverso set di regole.

Invece di fare in modo che una regola assuma un parametro e agisca in modo diverso, ho creato più regole dipendenti dallo schermo (ScreenOneRule, ScreenTwoRule, ecc.).

Ora userei la classe in questo modo:

public class ScreenOne {
    Translator translator;

    public Screen(Translator translator) { ... }

    public void onSomeEvent() { translator.maybeTranslate(); }
}

e vorrei creare il set di regole per ScreenOne nella mia configurazione IoC (questo è un tentativo di pseudo codice):

Rule[] screenOneRules = new Rule[] { SharedRule1, SharedRule2, ScreenOneRule1 }
ScreenOne screenOne = new Screen(new Translator(screenOneRules))

Questo risolve il mio problema, anche se mi sembra che il design non sia perfetto. Cosa succede se in seguito ottengo una regola che dipende da un valore noto solo dopo l'istanziazione dello schermo? Ad esempio, supponiamo che una regola debba conoscere le coordinate (x, y) di dove l'utente ha toccato sullo schermo per attivare onSomeEvent. Ho la sensazione che ScreenOne avrebbe bisogno di costruire le regole stesse in onSomeEvent . Penso che questo stia tornando alla mia domanda iniziale.

In tal caso, vorrei avere l'istanziazione delle regole da qualche altra parte. La soluzione è avere una fabbrica come questa?

public class RuleFactory {
    public Rule[] getRulesForScreenOne(int x, int y, String anotherParameter) {
        return new Rule[] { new SharedRule1(x, y), new SharedRule2(anotherParameter) };
    }
}

public void onSomeEvent() { 
    Rule[] rules = RuleFactory.getRulesForScreenOne(x, y, str);
    translator.maybeTranslate(rules);
}

A quel punto, Translator non riceverebbe regole nel suo costruttore. Non riesco a capirlo, ma mi sembra che l'implementazione della fabbrica non sia giusta. Qualcosa sul nome del metodo.

    
posta siger 27.03.2015 - 01:59
fonte

2 risposte

1

Suppongo che la tua classe Rule sia un esempio di modello di specifica o di comando. Regole decisamente diverse avranno bisogno di diversi tipi di informazioni per fare il suo lavoro, ma se fossi in me, non avrei passato quelle informazioni durante l'invocazione del metodo, avrei usato invece il costruttore. Le informazioni che sono diverse da una regola all'altra sono i dettagli di implementazione di quella regola, l'invocatore della regola non dovrebbe aver bisogno di saperlo, l'invocatore deve solo conoscere l'interfaccia pubblica della regola. La conoscenza per costruire la regola dovrebbe essere posta sul client o su un'altra classe.

Il client di MyClass è quello che passa la matrice di regole a MyClass, quindi può essere colui che crea la regola, o può delegare a una classe factory per creare la regola, o se si usa l'iniezione di dipendenza, si posso contare su IoC per iniettare la lista di regole per te. In entrambi i casi, i dati che differiscono tra ogni classe di regole devono essere passati in Rule in quel momento:

//Client
Rule[] rules = new Rule[2];
rules[0] = new AnExampleRule("some context information");
rules[1] = new AnotherRule(12);

MyClass myClass = new MyClass(rules);
    
risposta data 27.03.2015 - 03:14
fonte
0

In una situazione simile sto usando una macchina a stati. Il fatto che la tua regola abbia bisogno dell'origine dell'evento mi dice che la tua regola deve considerare il contesto dell'evento, cioè lo stato in cui ti trovi. Nel mio scenario macchina stato suggerito il tuo modello sarebbe come questo:

  • Tu rappresenti il "mondo" nella tua applicazione definendo i possibili stati con variabili quantificabili rilevanti. (ad esempio, il nome della schermata è 'xy' o userLoggedIn == true e così via).
  • Aggiungi una funzione generica che controlla lo stato in cui ti trovi.
  • Registri le transizioni possibili per ogni stato (possibili passaggi / eventi che porteranno la tua applicazione in un altro stato).
  • Crei la logica (una funzione euristica) per dare la priorità alle transizioni disponibili in modo che il codice del programma sceglierà uno degli eventi possibili.

Lo so, è una risposta breve rispetto alla dimensione dell'argomento, ma penso che tu possa continuare con queste parole chiave e saperne di più con l'aiuto di google.

Spero che questo aiuti.

    
risposta data 27.03.2015 - 20:21
fonte

Leggi altre domande sui tag