Ho riscontrato un problema interessante durante l'implementazione del pattern Observer con C++ e STL. Considera questo classico esempio:Problemi nell'implementazione del pattern "Observer"
class Observer {
public:
virtual void notify() = 0;
};
class Subject {
public:
void addObserver(Observer*);
void remObserver(Observer*);
private:
void notifyAll();
};
void Subject::notifyAll() {
for (all registered observers) { observer->notify(); }
}
Questo esempio può essere trovato in ogni libro su modelli di progettazione. Sfortunatamente, i sistemi di vita reale sono più complessi, quindi ecco il primo problema: alcuni osservatori decidono di aggiungere altri osservatori all'oggetto per essere notificati. Ciò invalida il ciclo "for" e tutti gli iteratori che uso. La soluzione è piuttosto semplice: creo un'istantanea dell'elenco degli osservatori registrati e iteriamo sull'istantanea. L'aggiunta di nuovi osservatori non invalida l'istantanea, quindi tutto sembra ok. Ma ecco che arriva un altro problema: gli osservatori decidono di distruggersi per essere notificati. Ancora peggio, un singolo osservatore può decidere di distruggere tutti gli altri osservatori (sono controllati dagli script) e ciò invalida sia la coda che un'istantanea. Mi trovo a ripetere su puntatori desolocati.
La mia domanda è: come devo gestire le situazioni, quando gli osservatori si uccidono a vicenda? Ci sono dei pattern pronti all'uso? Ho sempre pensato che "Observer" fosse il modello di design più semplice al mondo, ma ora sembra che non sia così facile implementarlo correttamente ...
Grazie a tutti per il vostro interesse. Prendiamo un riassunto delle decisioni:
[1] "Non farlo" Siamo spiacenti, ma è un must. Gli osservatori sono controllati dagli script e sono raccolti dalla spazzatura. Non riesco a controllare la garbage collection per impedire la loro de-assegnazione;
[2] "Usa boost: segnale" La decisione più promettente, ma non posso introdurre una spinta sul progetto, tali decisioni devono essere prese solo dal leader del progetto (stiamo scrivendo sotto Playstation);
[3] "Usa shared__ptr" Ciò impedirà agli osservatori di de-allocare. Alcuni sottosistemi possono fare affidamento sulla pulizia del pool di memoria, quindi non credo di poter utilizzare shared_ptr.
[4] "Postpone dealer dealer" Gli osservatori di coda per la rimozione durante la notifica, quindi utilizzare il secondo ciclo per eliminarli. Sfortunatamente, non posso impedire la deallocazione, quindi utilizzo una sorta di "adattatore" con l'involucro di observer, mantenendo in realtà la lista degli "adattatori". Sul distruttore, gli osservatori non assegnano i loro adattatori, quindi prendo il mio secondo ciclo per distruggere gli adattatori vuoti.
p.s. va bene, che modifico la mia domanda per riassumere tutto il post? Sono in Noob su StackOverflow ...
Buona domanda! Non avevo pensato di utilizzare il modello di osservatore in cui gli osservatori sono autorizzati a creare e distruggere altri osservatori del soggetto. –
Mi piace riassumere le risposte della domanda nella domanda, semplicemente non modificare la domanda originale con delezioni o lettori successivi potrebbero perdere sfumature della domanda originale (non che l'hai fatto, penso che la tua sintesi e la nota che è sintetizzata è superba). – Jay
Hai mai provato nessuno di questi per vedere quale ti è piaciuto di più o hai sentito il migliore? – prolink007