Con C, la struttura dei dati è e rimarrà stupida. Questo non è un giudizio di valore: intendo che se qualche codice ha l'indirizzo di treeNode *x
, può cambiare x->ITInfraResource->resourceStatus
senza che accada altro.
Incapsula dati
Per controllare questa situazione e rendere la tua struttura intelligente, devi prima incapsulare la tua struttura, in modo tale che il codice usando deve sempre usare alcune delle tue funzioni per cambiare lo stato.
In pratica, ci sono diversi modi per raggiungere questo obiettivo. Ad esempio, è possibile modulare la struttura utilizzando una compilazione separata. Nelle intestazioni useresti solo una dichiarazione in avanti della tua struttura Resource
. Ciò consente al codice che utilizza di utilizzare i puntatori a una risorsa, ma vieta di accedere direttamente ai suoi contenuti:
typedef struct Resource Resource;
void change_resource_status (treeNode *n, Status s); // must use to access content
E nell'unità di compilazione in cui verranno implementate change_resource_status()
e funzioni simili, potresti definire la struttura, mantenendola privata per il mondo esterno:
struct Resource {
char *resourceName;
Status resourceStatus;
};
Hai quindi il controllo di tutte le modifiche apportate e puoi propagare la modifica nella struttura utilizzando vari approcci
Strategie di propagazione
Se fossi in un design orientato agli oggetti, ti consiglierei forse un osservatore modello o un ciclo degli eventi utilizzando un comando modello. Ma in un mondo non-object, per una tale struttura ad albero raccomanderei definitivamente un approccio più semplice che non richiede l'uso di puntatori di funzione e funzioni di callback.
In base all'incapsulamento, il modo più semplice sarebbe iterare attraverso i nodi parent per aggiornarli direttamente o chiamando una funzione per ricalcolarli.
Due tecniche potrebbero minimizzare i ricalcoli:
-
ricalcolo condizionale: ad esempio, se il genitore deve essere aggiornato con lo stato massimo dei figli, allora il genitore deve essere aggiornato solo se il nuovo stato dei figli è superiore a quello vecchio (o se lo stato dei figli precedenti corrisponde al valore massimo del genitore).
-
ricalcolo asincrono: per qualsiasi modifica, è possibile impostare un flag dirty nel nodo che viene modificato e i nodi che devono essere ricalcolati. In una seconda fase, quando tutte le modifiche sono state apportate, o periodicamente, si passa attraverso l'albero e si ricalcolano tutti i nodi sporchi.
Se non puoi scegliere l'incapsulamento
Un modo per gestire le modifiche sarebbe quindi avere un secondo stato "ombra".
Ogni tanto (ad esempio utilizzando un approccio multi-threading?), un algoritmo di ricalcolo può esplorare i nodi in cui lo stato è diverso dallo stato dell'ombra (cioè i nodi "sporchi") e avviare il processo di ricalcolo.
Ma senza un'appropriata incapsulamento, l'algoritmo di ricalcolo rimarrebbe vulnerabile all'abuso dello stato dell'ombra.