Passare un oggetto di contesto al costruttore o al metodo

2

Sto scrivendo un parser di discesa ricorsivo.

Lascia che% co_de specifichi il contesto per l'analisi. Pseudocodice:

class ParseContext {
    Logger logger; // logger for error/warning messages
    Locale locale; // locale for error/warning messages
}

Le classi derivate da ParseContext possono specificare ulteriori opzioni di analisi, come la versione della grammatica o se gli avvertimenti devono essere considerati come errori.

Ora definisci ParseContext .

1.

class Parser {
    Parser(ParseContext context) {
        this.context = context;
    }
    abstract function parse(String text) return Object;
}

o

2.

class Parser {
    Parser() {
    }
    abstract function parse(ParseContext context, String text) return Object;
}

Quale delle classi Parser sopra è un modello noto e che è un antipattern noto? O forse, nessuno dei due è nel set di antipatterns conosciuti? Il primo sembra essere un esempio di modello di iniezione di dipendenza. Ciò implica che l'altro è un antipattern?

Sembra insolito chiamare Parser con diversi diversi argomenti .parse . Vuol dire che non ha senso rifattare "2" come opzione di costruzione invece (come in "1")?

Quali sono (dis) vantaggi di entrambi?

    
posta porton 05.08.2018 - 18:32
fonte

3 risposte

3

Né è un anti-modello, il primo è la scelta migliore però. È la classe parser nel suo complesso (potenzialmente tutti i suoi metodi) che dipende dal contesto. L'esempio mostra una classe con un solo metodo, ma potresti avere diversi e sarebbe rumoroso e non necessario passare il contesto a ciascuno.

    
risposta data 05.08.2018 - 18:52
fonte
2

Un parser di discesa ricorsivo è caratterizzato da funzioni ricorsive. Non è necessario coinvolgere classi (tranne ovviamente in linguaggi come Java, dove tutto deve essere inserito in una classe).

Tuttavia, passerai spesso del contesto attraverso tutte queste funzioni. Questo è di solito un argomento di funzione esplicito (la tua soluzione 2). Non c'è niente di sbagliato in questo, ma diventa rapidamente noioso se ci sono molti argomenti.

In alternativa, puoi trasformare le funzioni ricorsive in metodi ricorsivi. Ora l'oggetto invocante (" this ") può essere usato per memorizzare il contesto che è necessario in tutti i metodi di analisi (la soluzione 1, eccetto che Parser e ParseContext sono gli stessi). Di conseguenza, questo contesto dovrebbe essere fornito nel costruttore di oggetti di analisi (anche se di solito mantengo privato quel costruttore).

Questo è indicato quando hai un contesto che è veramente necessario per tutte o quasi tutte le funzioni di analisi. Ci sono alcuni scenari che possono complicare questo.

  • Se alcuni contesti hanno ambito per la durata di una funzione di analisi, si potrebbe essere tentati di modificare lo stato di contesto dell'oggetto corrente. La soluzione più semplice è creare un nuovo oggetto di analisi per l'ambito interno.

    Un esempio di dati con ambito potrebbe essere una tabella di simboli per variabili locali in un parser del linguaggio di programmazione, o informazioni locali quando si analizza un documento strutturato ad albero (come HTML) o informazioni di indentazione in un linguaggio sensibile al layout - nonostante il layout le informazioni potrebbero essere meglio passate come argomento di funzione, non come stato dell'oggetto parser.

  • Molto spesso la lingua che viene analizzata è composta da più lingue secondarie, ad es. un'istruzione-grammatica e un'espressione-grammatica in un linguaggio di programmazione o un parser DTD come parte di un parser XML. Queste lingue secondarie possono richiedere diversi tipi di contesto.

    In casi semplici, è possibile fornire semplicemente un contesto aggiuntivo come parametri di metodo aggiuntivi. Se il sotto-contesto richiesto è diverso e non solo un superset, prendi in considerazione la creazione di più classi parser che cooperano. Dovresti quindi creare un oggetto sub-parser per analizzare una sottolingua.

risposta data 05.08.2018 - 21:28
fonte
0

Contrariamente alle risposte precedenti, c'è un motivo per passare il contesto come argomento del metodo .parse() piuttosto che tramite il costruttore.

Considera il seguente codice Python (da un vero progetto open source):

class ZeroOrMorePredicate(PredicateParser):
    def __init__(self, predicate, child):
        super().__init__(predicate)
        self.child = child

    def parse(self, parse_context, graph, node):
        iter = graph.objects(node, self.predicate)
        return [self.child.parse(parse_context, graph, elt) for elt in iter]

Questo parser analizza una serie di predicati RDF relativi a un determinato nodo (non è necessario sapere quali sono i predicati e i nodi per comprendere l'idea) e restituisce i risultati dei predicati di analisi come un elenco.

Ricordo che def definisce i metodi in Python, in particolare def __init__ è un costruttore.

Se rifattore per passare il contesto tramite costruttori, gli utenti di questa classe dovrebbero passare il contesto due volte: nel costruttore di ZeroOrMorePredicate e nel costruttore del parser di child .

In questo modo il refactoring suggerito in altre due risposte un po 'complica la vita degli utenti della classe. Quindi, probabilmente potrei decidere di ignorare altre due risposte e non fare refactoring come consiglio.

    
risposta data 10.11.2018 - 21:52
fonte

Leggi altre domande sui tag