Domanda

Ho un gestore di eventi su una classe di limite che gestisce un meccanismo di persistenza per una determinata transazione generica:

void MyBoundaryClass::MyEventHandler(...)
{
  //retrieve stuff from the UI
  //...
  //declare and initialize trasaction to persist
  SimpleTransaction myTransaction(.../*pass down stuff*/);
  //do some other checks
  //...
  //declare transaction persistor
  TransactionPersistor myPersistor(myTransaction, .../*pass down connection to DB and other stuff*/);
  //persist transaction
  try
  {
    myPersistor.Persist();
  }
  catch(...)
  {
    //handle errors
  }
}

Sarebbe meglio avere una sorta di TransactionManager per avvolgere gli oggetti SimpleTransaction e TransactionPErsistor?

Esiste qualche regola empirica utile per capire se ho bisogno di un ulteriore livello di incapsulamento?

Al momento la regola empirica che seguo è "se il metodo diventa troppo grande - fai qualcosa al riguardo". A volte è difficile trovare il giusto equilibrio tra procedurale e orientato agli oggetti quando si ha a che fare con gestori di eventi di confine.

Qualche opinione?

Saluti

È stato utile?

Soluzione

Considerando che:

  • il concetto di incapsulamento riguarda la definizione di un contenitore e
  • il design orientato agli oggetti si basa sul concetto di passaggio di messaggi (invocazione di metodi)

Direi che il API è una buona indicazione della pertinenza di un nuovo incapsulamento di alto livello (ovvero la definizione di un nuovo oggetto)

Se i servizi (ovvero l'API) offerti da questo nuovo oggetto sono coerenti e sono meglio esposti al resto del programma quando raggruppati in un oggetto speciale, quindi utilizzare un nuovo oggetto.

Altrimenti, è probabile che sia eccessivo.

Poiché si espone un'API pubblicità creando un nuovo oggetto, la nozione di test potrebbe essere più facile da fare all'interno di quel nuovo oggetto ( e alcuni altri oggetti finti ), piuttosto che creare molti oggetti legacy per testare quelle stesse operazioni.

Nel tuo caso, se vuoi testare la transazione, devi effettivamente testare MyEventHandler di MyBoundaryClass, al fine di recuperare i dati dall'interfaccia utente.

Ma se si definisce un TransactionManager, ciò offre l'opportunità di abbinare i diversi livelli di architettura (GUI vs. dati) presenti in MyBoundaryClass e di esportare la gestione dei dati in una classe dedicata. < br> Quindi, è possibile testare la persistenza dei dati in uno scenario di test indipendente, concentrandosi in particolare su valori limite, errore del database e condizioni non nominali e così via.

Lo scenario di test può aiutarti a perfezionare la coesione (ottimo punto menzionato da Daok ) dei tuoi diversi oggetti. Se i test sono semplici e coerenti, è probabile che i tuoi oggetti abbiano un limite di servizio ben definito.

Poiché si può sostenere che Accoppiamento e coesione sono due cardini della programmazione OO , la coesione di una nuova classe come TransactionManager può essere valutata in base all'insieme di azioni che eseguirà.

  

Coesivo significa che una determinata classe esegue una serie di azioni strettamente correlate. Una mancanza di coesione, d'altra parte, significa che una classe sta eseguendo diversi compiti non correlati. [...] il software applicativo alla fine diventerà ingestibile man mano che sempre più comportamenti si disperdono e finiscono in posti sbagliati.

Se riorganizzi comportamenti altrimenti implementati in diverse posizioni nel tuo TransactionManager, dovrebbe andare bene, a condizione che la sua API pubblica rappresenti passaggi chiari di ciò che comporta una transazione e non "elementi relativi alla transazione". come varie funzioni di utilità. Un nome in sé non è sufficiente per giudicare la coesione di una classe. È necessaria la combinazione del nome e della sua API pubblica.

Ad esempio, un aspetto interessante di un TransactionManager sarebbe incapsulare completamente la nozione di Transaction, che dovrebbe:

  • diventa praticamente sconosciuto dal resto del sistema e ridurrebbe l'accoppiamento tra le altre classi e "Transazione"
  • rafforza la coesione di TransactionManager centrando la sua API attorno ai passaggi della transazione (come initTransaction (), persistTransaction (), ...), evitando qualsiasi getter o setter per qualsiasi istanza Transaction.

Altri suggerimenti

Elaborando il suggerimento di VonC, considera le seguenti linee guida:

  • Se si prevede di invocare le stesse funzioni altrove, allo stesso modo, è ragionevole incapsularle in un nuovo oggetto.

  • Se una funzione (o un oggetto) fornisce una serie di servizi utili individualmente, è ragionevole trasformarlo in componenti più piccoli.

Il punto di VonC sull'API è un eccellente tornasole: creare interfacce efficaci e gli oggetti diventano spesso evidenti.

Il livello di incapsulamento dovrebbe essere direttamente collegato alla coesione del tuo oggetto. Il tuo oggetto deve svolgere una sola attività o deve essere diviso in più classi e incapsulare tutti i suoi comportamenti e proprietà.

Una regola empirica è quando è il momento di testare il tuo oggetto. Se stai facendo Unit Testing e ti rendi conto che stai testando più cose diverse (non nella stessa area di azione) di quanto potresti provare a dividere.

Nel tuo caso , incapsulerei con la tua idea di " TransactionManager " ;. In questo modo il " TransactionManager " gestirà il funzionamento della transazione e non " MyBoundaryClass " ;.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top