Come mitigare la complessità dei fallback introdotti per riprovare automaticamente in caso di fallimento?

1

Quindi può sembrare un po 'ampio e generalizzato, ma dopo aver passato un po' di tempo a lavorare con gli sviluppatori, ho notato molti metodi e schemi di design diversi. Tuttavia, una cosa importante che mi viene in mente è come la complessità di alcune di queste classi relativamente semplici (per quanto riguarda la logica) possa essere ingombrante.

Ad esempio, di recente ho creato un servizio che sincronizza i dati tra SalesForce e Zendesk. Ora ci sono molte regole aziendali che devono essere prese in considerazione, ma ho creato l'applicazione attorno al pattern di codice MVVM.

Ma una volta approfondito un po ', noterai molta complessità e inizierai a scavare attraverso molte classi per capire cosa sta succedendo.

Ad esempio, ecco un diagramma di classe del mio strumento di sincronizzazione.

Anche uno sguardo casuale sarà in grado di mostrare un po 'della complessità, e diventa ancora più difficile quando si scava nel codice reale dietro i singoli metodi.

Non solo stai effettuando l'interfaccia tra due API separate, ma stai anche interagendo con più protocolli (SOAP e REST). Entrambi questi servizi sono soggetti a singhiozzo e malfunzionamenti, pertanto è necessario ricorrere automaticamente al tentativo di riprovare in caso di errore. Quindi è necessario mappare tutti i campi correttamente avanti e indietro. Da lì devi assicurarti che nient'altro riesca a fallire. Ogni singolo campo è una possibilità di fallimento, e ogni funzionalità aggiunta crea in modo esponenzialmente complesso.

Come puoi mitigare parte di questa complessità?

    
posta JD Davis 18.11.2015 - 21:41
fonte

2 risposte

2

But once you dig in a bit deeper, you'll notice a lot of complexity and you start digging through a lot of classes to figure out quite what's going on.

Dal mio punto di vista hai il problema opposto. Hai un sentimento di complessità perché ci sono troppe poche classi .

La complessità non deriva da retry pattern , la complessità è il prezzo da pagare per non utilizzare uno strumento OOP essenziale: inheritance .

Senza un po 'di codice è difficile mettere in evidenza molti difetti (potresti prendere in considerazione la pubblicazione di parti rilevanti sulla revisione del codice) ma poche cose da considerare:

  • Convenzione di denominazione se non uniforme: diverso involucro (ad esempio caso cammello per alcuni metodi privati), nomi come SfAttach e SfAttach2 . Nome in minuscolo per proprietà e caratteri di sottolineatura qui e là. Attenersi strettamente alle convenzioni / migliori pratiche di ambiente . Considera anche di eliminare prefissi ( Sf , Zd , E ) quando possibile.
  • Hai molte lezioni di Dio e molte classi di aiuto . Di solito indicano che c'è qualcosa di sbagliato nel tuo design. Come ora, hai diviso un bel po 'di codice in quattro classi con alcune entità pure attorno a loro. Considera anche l'uso di classi di raccolta . Hai una lista di Attachment ? Probabilmente hai bisogno di una classe AttachmentCollection . Non estendere i dettagli di implementazione in tutto il codice.
  • Segui gli schemi comuni (inseriscili quando non li usi) e implementali correttamente: ad esempio, vedo IDisposable implementato senza un metodo% / protected% privato protetto.
  • Direi anche che i metodi sono lunghi e complessi (vedo pochi metodi privati ma penso che ci sia molto da fare, allora la lunghezza dei metodi deve essere alta). Dividi il tuo codice! Voglio capire che cosa fa un metodo dal suo nome e come lo fa leggendo 5/10 righe di codice, non di più.

Che cosa fare?

  • Prima di tutto dovresti descrivere tutto ad altissimo livello. Non importa che tu stia usando Salesforce e Zendesk. Fallo scrivendo i tuoi test e pensa di dover mantenere sincronizzati due diversi servizi. Nessun nome, nessun dettaglio di implementazione.
  • Crea classi astratte di alto livello con tutta la logica di cui hai bisogno. Questo è il tuo dominio, popola con entità di alto livello. Ogni volta che ritieni di aver bisogno di una classe helper allora stai facendo qualcosa di sbagliato, torna indietro.
  • Implementare i mock per completare i test. Saprai che la tua logica è giusta, indipendentemente dall'attuazione concreta. Con i mock è possibile simulare errori di rete ...
  • ... è il momento di implementare un buon pattern di ripetizione . Se hai fatto tutto bene, devi solo aggiungere questa complessità a un singolo metodo Dispose(bool) e un metodo ReadField() (o ai loro equivalenti se lavori a livello di record).
  • Ora puoi derivare classi specifiche di implementazione. WriteField() e Zendesk . Riempi il vuoto tra la rappresentazione astratta e l'effettiva implementazione.
  • Aggiungi test specifici per i servizi che stai utilizzando. Puoi anche prenderli in giro uno per uno per simulare errori e strane condizioni.
  • Il gioco è fatto.

Vedrai che hai molte più classi (probabilmente con meno metodi) ma per capire cosa sta succedendo hai solo bisogno di leggere poche righe di codice ...

    
risposta data 24.11.2015 - 15:54
fonte
1

Crea classi (o astrazioni) come necessario che gestiscono ciascuno uno dei bit complicati. Ad esempio, potresti avere una classe che farà del suo meglio per inviare un messaggio dalla tua applicazione a un determinato server e ottenere una risposta, e avrà esito positivo o negativo e ti dirà quale, e gestirà eventuali errori a tal punto che se fallisce, il chiamante accetta l'errore e non prova nient'altro.

È difficile da implementare, ma è difficile da implementare non perché usi una strategia o uno schema sbagliato, ma perché è solo difficile. Ma con questa interfaccia, la classe è facile da usare: invia un messaggio, sul processo di successo la risposta, sul processo fallito l'errore.

E lo ripeti, scrivendo più classi che ognuna di loro è tanto complicata come dovrebbe essere e non più, e ognuna di esse è facile da usare.

    
risposta data 24.11.2015 - 15:19
fonte

Leggi altre domande sui tag