Il mio lavoro e i miei interessi personali spesso mi scrivono codice analizzando , cioè codice che converte da una stringa di input scritta in un determinato linguaggio formale in un albero di oggetti del modello.
Vengo da un sottotitolo in linguaggio C, e la strategia per farlo in C è piuttosto semplice:
struct my_object *parse(const char *input);
Questa è una semplice visione delle cose, ma ottieni il punto: questa singola API può fare tutto ciò che vuole internamente, purché converta la stringa data in un oggetto modello.
Generalmente, la funzione crea internamente una struttura di variabili di stato che sono significative solo durante il processo di analisi. Questa struttura è condivisa con funzioni interne e private:
static void parse_a(struct parsing_state *parsing_state);
static void parse_b(struct parsing_state *parsing_state);
static void parse_c(struct parsing_state *parsing_state);
/* ... */
struct my_object *parse(const char *input)
{
struct parsing_state parsing_state;
/* ... */
parse_a(&parsing_state);
/* ... */
parse_b(&parsing_state);
/* ... */
parse_c(&parsing_state);
/* ... */
return my_object;
}
Credo che la programmazione procedurale sia una scelta perfetta per questo compito: convertirlo in questo. Non ho bisogno di un'istanza dell'oggetto "parser" qui. Quando ho bisogno di implementare questo compito in un linguaggio OOP (principalmente, C ++, Java e Python), finisco sempre con questo schema (Python usato per i seguenti esempi):
class Parser:
def _reset(self, init_state, input_string):
self._state = init_state
self._input_string = input_string
# ...
def parse(input_string):
self._reset(23, input_string)
# ...
self._parse_a()
# ...
self._parse_b()
# ...
self._parse_c()
# ...
return my_object
E quindi, per analizzare una stringa di input effettiva:
parser = Parser()
my_object = parser.parse(input_string)
In altre parole, un oggetto parser viene creato dall'utente solo per contenere lo stato di analisi corrente e quindi scartato. Credo che la creazione di questa istanza dal lato utente sia inutile. Ovviamente può essere spostato:
def parse(input_string):
parser = Parser()
return parser.parse(input_string)
Tuttavia, trovo che l'oggetto parser, anche se nascosto all'utente, sia inutile, perché è vivo solo per il processo di analisi. Ancora più importante, questo metodo _reset()
di cui sopra ha bisogno di esistere in qualche modo, perché non vi è alcuna garanzia che l'oggetto parser non possa essere riutilizzato con un'altra stringa di input, pertanto le variabili di stato precedenti devono essere reinizializzate. L'ideale sarebbe inizializzare lo stato nel costruttore e garantire che il metodo parse()
venga chiamato solo una volta.
Il modo procedurale è sempre disponibile in tutti quei linguaggi: semplici funzioni a livello di modulo in Python, funzioni in stile C o metodi statici in C ++ e metodi statici in Java. Tutte queste funzioni possono passare una semplice struttura di dati vecchi mantenendo lo stato di analisi corrente a funzioni private / metodi statici, che viene scartato prima di restituire l'oggetto del modello finale.
La mia domanda è: esiste un modo idiomatico per raggiungere questo compito senza fare affidamento su funzioni non membro / metodi statici nei linguaggi OOP?