2012-01-04 19 views
8

Nel suo talk al minuto 54:53, Rich Hickey sta parlando dell'utilizzo delle code come mezzo per separare parti del programma dipendenti. Mi può dare un esempio su come deouple il seguente pezzo di Java-pseudo-codice al fine di migliorare il suo design e/o la flessibilità:Disaccoppiare i programmi utilizzando le code

// Warning: Java-pseudo-code ahead 
class Job { 
    public void doRun(A a) { 
     saveObjectToDatabase(a); 

     B b = computeB(a); 
     saveObjectToDatabase(b); 

     C c = computeC(b); 
     logToFile(c); 
    } 
} 

saveObjectToDatabase e saveObjectToDatabase può essere visto come un metodo con effetti collaterali , mentre l'output di computeB e computeC dipende solo da a.

So che questa domanda è piuttosto vaga/ampia. Mi piacerebbe avere un'idea di come sfruttare i meccanismi di accodamento senza complicare enormemente il mio programma e assicurandomi che faccia la cosa giusta nell'ordine giusto. Tutti i puntatori nella giusta direzione sono apprezzati.

+0

Non è molto chiaro. L'esempio che stai mostrando ha un metodo autonomo, che non usa alcuna dipendenza esterna. Non c'è nulla da disaccoppiare in quanto il lavoro non è accoppiato a nulla. –

+0

La mia ipotesi è che @Matt vorrebbe ottenere thread diversi che eseguono i metodi computeB() e computeC(), usando le code per spostare le unità di lavoro tra i diversi thread. – Rich

+0

Controllare [Modelli di integrazione aziendale] (http://www.eaipatterns.com/). – TrueWill

risposta

3

Beh, non è un ottimo esempio, ma (nel design più diretto) avresti praticamente due code e (a seconda della quantità di dati coinvolti) potresti omettere il database.

Un primo processo sarebbe ricevere i vostri a oggetti dal "mondo esterno" e li accodare nella coda 1. Un secondo processo sarebbe annullare l'accodamento oggetti dalla coda di 1, eseguire computeB, e accodare i risultati sulla coda 2. Un terzo processo sarebbe deselezionare gli oggetti dalla coda 2, eseguire computeC e registrare il risultato o qualsiasi altra cosa.

A seconda, come ho detto, della quantità di dati coinvolti (e forse anche di altri fattori) gli "oggetti" passati nelle code potrebbero essere gli attuali oggetti a e oppure solo token/chiavi per trovare il dati nel database.

Le code stesse potrebbero essere implementate in diversi modi. È possibile implementare una coda con un database, ad esempio, anche se i dettagli diventano confusi. I "processi" potrebbero essere attività Java all'interno di un singolo processo Java o potrebbero essere processi OS separati, possibilmente anche su macchine separate.

Quando si usano "pipe" su Unix si utilizzano effettivamente le code in questo modo.

1

Questo è esattamente il principio utilizzato da una libreria java che sto usando. L'idea è di avere componenti assegnati a singole attività nei programmi (il logger è un esempio perfetto). Ora ogni componente deve essere eseguito indipendentemente dagli altri, sia come thread sia come gestore di eventi.

Nel caso di evento, ogni componente notifica quali tipi di eventi \ messaggi desidera ascoltare. Si dispone di un dispatcher che raccoglie i messaggi in arrivo e li inserisce nella coda dei destinatari. Il destinatario processa e alla fine genera nuovi messaggi. Ed ecc ...

Nel tuo caso, qualcosa di simile:

class SaveObjectHandler{ 
// 
void handle(Event e, Object o){ 
    if(e instanceof SaveEvent) 
     saveObjectToDatabase(o); 
} 

}; 

class TransformObject{ 
// 
void handle(Event e,Object o){ 
    if(e instanceof TransformEvent){ 
     B result = compute(o); 
     send(new SaveEvent(),result) 
    } 

} 

}; 

class Logger{ 

    void handle(Event e, Object o){ 
     if(o instanceof B) 
     //perform computeC 
     logEvent((B)o); 
    } 

}; 

};

La libreria in questione è SEDA.

0

Ho paura che con i metodi saveObject con effetti collaterali non è possibile disaccoppiarli bene o almeno non facilmente.

Ma diciamo che è necessario scrivere velocemente nel database alcuni oggetti.La mia opinione è che il modo più veloce con DB relazionale dovrebbe essere quello di salvare gli oggetti in una coda da più client e di raccoglierli da uno o due scrittori piuttosto veloci che spingono i dati nel database il più velocemente possibile.

0

Per ragioni di completezza, vorrei aggiungere qualche informazione in più per Hot Licks' risposta:

Ho fatto ulteriori ricerche su questo argomento e, infine, sono giunto alla conclusione, che districare il metodo è la strada da percorrere. Userò la terminologia kafka dei produttori/consumatori/argomenti. Per ulteriori informazioni vedere The Log: What every software engineer should know about real-time data's unifying abstraction e, in particolare, questo grafico:

enter image description here

Per quanto riguarda la mia domanda specifica dell'esempio pubblicato, ci sono due modi per risolverlo:

Soluzione 1

  • Consumatore 1:
    • consumo per m argomento a
    • salvare nel database.
  • dei consumatori 2:
    • consumano dal tema a
    • calcolo b
    • Salva per database.
  • Consumer 3: consumare da argomento a
    • calcolo b
    • calcolo c
    • Salva nel database

Questo ha lo svantaggio di calcolo bdue volte. In pseudo-codice:

class ConsumerA { 
    public void consume(A a) { 
     saveObjectToDatabase(a); 
    } 
} 

class ConsumerB { 
    public void consume(A a) { 
     B b = computeB(a); 
     saveObjectToDatabase(b); 
    } 
} 

class ConsumerLog { 
    public void consume(A a) { 
     B b = computeB(a); 
     C c = computeC(b); 
     logToFile(c); 
    } 
} 

Soluzione 2

  • Consumer 1:
    • consumano dal tema a
    • Salva per database.
  • dei consumatori 2:
    • consumano dal tema a
    • calcolo b, salvare in banca dati
    • pubblicare b ad un argomento a parte b.
  • Consumer 3:
    • consumano dal tema b
    • calcolo c
    • LogToFile c

In pseudo-codice:

class ConsumerA { 
    public void consume(A a) { 
     saveObjectToDatabase(a); 
    } 
} 

class ConsumerB { 
    public void consume(A a) { 
     B b = computeB(a); 
     saveObjectToDatabase(b); 
     publish(b); // republish computed information to another topic b 
    } 
} 

class ConsumerLog { 
    public void consume(B b) { 
     C c = computeC(b); 
     logToFile(c); 
    } 
}