Astrazione su Pattern iteratore?

3

Recentemente, ho avuto il compito di implementare un progetto di ricerca in cui dovevo leggere un file XML. Il file XML conteneva un elenco di messaggi che dovevo inviare ad un altro sistema su una porta specifica. Ogni messaggio ha un tipo che può essere o ricevere / trasmettere. Ricevere significa che devo ricevere un messaggio da qualche porta e trasmettere significa che devo inviare un messaggio. xml rappresenta il messaggio che devo inviare / ricevere. Esempio di tale XML è il seguente

<Messages>
  <Message name="A" xml="A.xml" port="PortA" type="receive" time="0" />
  <Message name="B" xml="B.xml" port="PortA" type="transmit" time="11" />
  <Message name="C" xml="C.xml" port="PortB" type="receive" time="0" />
  <Message name="D" xml="D.xml" port="PortB" type="transmit" time="0" />
</Messages>

Per gestire questo, ho usato pattern iteratore con metodi semplici come getNextMessage (), hasNextMessage () e loadFile (). Questa astrazione ha funzionato bene e non sono state effettuate altre classi se ho modificato qualcosa nelle mie classi di gestione dei messaggi (MessageHandler).

Poiché i requisiti continuavano ad aumentare, ho dovuto aggiungere altri metodi alla mia classe MessageHandler per gestire scenari complessi. Quindi, ho aggiunto alcuni altri metodi

getNextMessageWithoutIterator () : per impostazione predefinita getNextMessage () incrementava internamente un puntatore che puntava sempre al messaggio corrente in elaborazione. Ma venne un requisito che mi impone di avere conoscenza di 1 messaggio in anticipo (se stavo elaborando il Messaggio A, allora questo requisito richiedeva anche la conoscenza del prossimo Messaggio B). Quindi, questo metodo mi dà il messaggio successivo ma senza incrementare il puntatore / iteratore.

incrementToNextReceive () - Poiché si trattava di un'applicazione che comunicava tra più sistemi, a volte i messaggi venivano ricevuti prima del previsto. Quindi, il mio ordine di elaborazione è stato disturbato. Per gestirlo, ho aggiunto questo metodo che crea internamente un altro puntatore. Questo puntatore è impostato sul messaggio fuori servizio. Quindi, quando il mio flusso normale arriverà a questo puntatore, ignorerà questo messaggio perché lo abbiamo già ricevuto ed elaborato.

e alcuni altri. Ti viene l'idea che ciò che è iniziato come una semplice classe ora è cresciuto in una grande classe cercando di facilitare tutti gli scenari possibili. Ora, recentemente abbiamo deciso che la struttura attuale del file XML non è sufficiente e ci siamo spostati su una struttura più complessa. Questa struttura di esempio rappresenta più o meno ciò che era nella vecchia struttura. Transizione = ricevi il messaggio del tipo e Invia è il messaggio del tipo di trasmissione.

<Sequence>
    <Transition stateName="S0" port="portA" match="A.xml" nextState="S1">
        <Send message="B.xml" port="portA" timeDelta="0" />
    </Transition>
    <Transition stateName="S1" port="portB" match="C.xml" nextState="S2">
        <Send message="D.xml" port="portB" timeDelta="1" />
    </Transition>
</Sequence>

Ora vogliamo supportare entrambi i tipi di file XML. Il modo in cui accediamo ai messaggi è molto diverso in entrambi i file XML. La vecchia struttura era sequenziale dove iniziamo dal Messaggio A e andiamo fino al Messaggio Z. Ma nella nuova struttura, il flusso di codice entrerà in qualsiasi Transizione che soddisfi tutti i requisiti.

Problema: Nel mio codice, stavo usando MessageHandler in diversi luoghi come ProcessMessages. Ora, dal momento che altri requisiti continuavano ad aggiungere, MessageHandlar divenne meno astratto e altre classi iniziarono a dipendere e conoscere il funzionamento interno di MessageHandler che creava dipendenza. Così,

Quale può essere la soluzione del mio attuale problema. Dovrei attenermi al Pattern Iterator e in qualche modo cercare di creare un'astrazione su di esso. Qualcosa come GenericHanlder (interfaccia), e quindi implementare 2 iteratori OldMessageHanlder e NewMessageHandler? Ma per quanto riguarda i nuovi metodi che ho implementato per gestire requisiti aggiuntivi perché quei metodi non faranno parte di NewMessageHandler o saranno gestiti in qualche altro modo.

o

Dovrei aggiungere un'astrazione su ProcessMessages come GenericProcessMessages e implementare le interfacce usando OldProcessMessages e NewProcessMessages e OldProcessMessage userà OldMessageHandler e NewProcessMessages userà NewMessageHandler per elaborare i messaggi?

Sono ancora uno studente e questa è un'esperienza di apprendimento per me mentre si migliora la qualità del software. Quindi, qualsiasi aiuto sarebbe fantastico.

    
posta Behroz Sikander 24.02.2016 - 17:10
fonte

2 risposte

5

Se vuoi scrivere un iteratore in C ++, dovrebbe (IMO) seguire le norme per gli iteratori C ++ in generale. Nel caso di lettura di dati da un file, di solito è possibile farlo senza scrivere un vero iteratore. Invece, scrivi operator>> per leggere un singolo elemento da un file (e, se necessario, un operator<< per scrivere un singolo elemento in un file).

Quindi puoi usare un std::istream_iterator per scorrere su un file di input (e un std::ostream_iterator per un file di output, se necessario).

Cose come incrementToNextReceive non dovrebbero essere parte dell'iteratore stesso. Invece dovresti usare qualcosa come std::find_if sul tuo iteratore di input per trovare l'elemento successivo che soddisfi i criteri di cui hai bisogno 1 .

Per quanto riguarda getNextMessageWithoutIterator , questa funzionalità è gratuita se si utilizzano normali iteratori di C ++. In particolare, l'avanzamento dell'iteratore è sempre disaccoppiato dal dereferenziamento dell'iteratore - l'avanzamento utilizza operator++ e il dereferenziamento utilizza operator * (anche se è necessario eseguire entrambi, *++it o *it++ sono entrambi normalmente supportati).

Per quanto riguarda il tuo nuovo formato XML, potresti quasi sicuramente implementare la maggior parte del tuo processo con Boost State Chart . Almeno in base a ciò che mostri qui, tuttavia, potrebbe essere eccessivo: l'elaborazione sembra abbastanza semplice da consentire a maggio di implementare più facilmente l'elaborazione piuttosto che imparare a sufficienza sul diagramma di stato per ottenerlo per fare il lavoro.

1. In alternativa, considera l'utilizzo di una classe Range e crea una filtered_range. Questa è un'impresa molto più grande se lo fai da zero. Se lo vuoi, ti consiglio di dare un'occhiata a Range V3 di Eric Neibler .

    
risposta data 24.02.2016 - 18:15
fonte
4

Il problema degli ordini può essere risolto con una coda d'ordine. Basta solo memorizzare un numero sufficiente di record per ottenere i record fuori ordine nella coda e deselezionare il successivo nell'ordine corretto. Lo stesso con la struttura complessa; hai bisogno di un buffer abbastanza grande da contenere tutti i punti di transizione validi.

getNextMessageWithoutIterator() è solo una funzione Peek() ; quelli sono abbastanza comuni negli scenari iteratori. Peek() ti consente di vedere il prossimo record senza far avanzare il puntatore.

    
risposta data 24.02.2016 - 17:48
fonte

Leggi altre domande sui tag