Progettazione OO in una pipeline di elaborazione dati

5

Mi chiedo come progettare una classe abbastanza semplice le cui proprietà sono complesse da calcolare. Inoltre, le proprietà dipendono l'una dall'altra per il calcolo.

Un esempio che utilizza grafici e elaborazione grafica (pensa a nodi e spigoli, non a grafici o grafici a dispersione) per motivare il problema:

Class CommunityGraphSet è una raccolta di Community istanze. CommunityGraphSet è inizializzato con un grafico di input (un grande grafico di social network), insieme ad alcuni parametri di base che descrivono ciò che è noto sul grafico. Un Community è un sottografo del grafico di input, insieme ad alcuni descrittori strutturali.

Dopo l'inizializzazione, CommunityGraphSet non contiene sottografi Community . Dopo diversi passaggi di elaborazione principali, contiene un set di Community sottografi. Ciascuna fase di elaborazione principale (cioè chiamata di funzione) attiva una GUI per un essere umano per regolare i parametri dell'algoritmo da eseguire in quella fase. I parametri set umani, i risultati di elaborazione vengono visualizzati e continuano a perfezionare i parametri finché i risultati non sono accettabili. Dopo aver "accettato i parametri" con un clic, i risultati che hanno visto vengono restituiti dal metodo che ha attivato la GUI.

input_graph = read("graph_file.csv")

number_communities = 3
sparseness_thresh = (0.05, 0.10)


# MAJOR PROCESSING STEP
# get parameters for key community member identification based on overall graph structure
key_ident_params = compute_key_identification_params(input_graph)

# initialize the graph set
graph_set = CommunityGraphSet(input_graph, number_communities, sparseness_thresh)

# MAJOR PROCESSING STEP
# mark where community subgraphs definitely are not.
partition_mask = isolate_potential_communities(graph_set)

# use mask to extract subgraphs for refinement by different algorithms.
potential_community_subgraphs = graph_set.extract_subgraphs(partition_mask)

for community in community_list:

    # MAJOR PROCESSING STEP
    # key community member extraction 
    key_members = extract_key_members(community, key_ident_params)

    # MAJOR PROCESSING STEP
    # use identified key members and community sparseness to trim community to final subgraph
    final_community = refine_community(community, key_members, sparseness_thresh)

    # put the refined community in the final set
    graph_set.add_community(final_community)

save(graph_set.serialize(), "communities.json")

I miei problemi con questo design:

  • CommunityGraphSet è stato. Contiene Community sottografi solo dopo che la funzione giusta ha chiamato nell'ordine corretto.
  • La pipeline di elaborazione è monolitica. Man mano che la pipeline di elaborazione cresce e cambia, mantenerla diventerà poco soddisfacente visto che mi trovo di fronte a un numero crescente di variabili utilizzate in un numero sempre maggiore di luoghi.

Questi sono davvero problemi? Oppure sono inerenti alle pipeline di elaborazione dei dati?

Se sono problemi, come posso risolverli?

Note:

  • Nessuno di CommunityGraphSet non è accoppiato alla logica della GUI, ma è accoppiato a un'interfaccia IterativeOptimizer per ogni passo. La GUI descritta o un algoritmo di ottimizzazione intelligente potrebbero implementare tale interfaccia.
posta kdbanman 13.05.2015 - 00:38
fonte

1 risposta

2

Penso che i due problemi elencati non derivino dal tuo progetto, ma piuttosto dal problema che stai risolvendo.

A seconda di quanto generico vuoi realizzare il tuo progetto, potresti avere alcuni metadati che definiscono ogni passaggio di elaborazione principale, e poi alcuni metodi che chiami per "spingere" la pipeline nel passaggio successivo. Naturalmente, dal momento che sembra che ogni passaggio richieda i propri parametri specifici dell'utente, dovresti definire i parametri anche genericamente, utilizzando più metadati.

Non penso che ciò che ho appena delineato sia un design particolarmente valido basato sulla descrizione del tuo problema. Aggiunge un sacco di complessità e overhead dei metadati senza vantaggi reali. A volte un problema è così specifico del dominio che il codice che hai progettato per risolverlo sarà ugualmente specializzato. Non è necessariamente una brutta cosa. Puoi mitigarlo scrivendo il codice che è il più chiaro, semplice e ben documentato possibile: -)

Aggiornamento: per quanto riguarda la tua domanda specifica se è "OK avere una classe che è progettata per essere incompleta in fase di inizializzazione e completata dai passaggi di elaborazione necessari", potresti pensare al consigli più generali menzionati in alcune linee guida di progettazione OO :

A properly written constructor leaves the resulting object in a valid state.

Ricorda, sta a te decidere cosa significhi "valido" nel contesto. Nel tuo esempio, direi che il CommunityGraphSet è in uno stato valido anche all'inizio perché il suo scopo non è quello di servire come rappresentazione statica immutabile dei grafici, ma come una pipeline di elaborazione per generare i grafici basati su fasi successive dell'input dell'utente. In altre parole, non è veramente "incompleto" perché i requisiti impongono che l'elaborazione richieda diverse fasi di input dell'utente. Forse un nome migliore sarebbe CommunityGraph Generator o CommunityGraph Processore . Inoltre, è possibile definire una classe separata CommunityGraph Set che mantenga i risultati dell'intero processo una volta terminata la pipeline. Anche se questa classe è contenuta all'interno di quella più grande, potrebbe rendere il design più comprensibile per un purista OO.

    
risposta data 13.05.2015 - 02:18
fonte

Leggi altre domande sui tag