Approccio per refactoring di una funzione che ha un sacco di braching speciale

2

Sto scrivendo una funzione che opera in modo leggermente diverso a seconda dei dati trasmessi. Il codice comune e univoco è mescolato. Non mi piace quello che ho e sto cercando un modo migliore. Si spera che questo caso dimostri la situazione. Considera che ogni riga di stampa di seguito è solo un proxy per una o più righe di codice più complesso.

def myFunc(a, b):
    if a=='mode1':
        print "special mode stuff"
    print "do for any mode"
    if a=='mode1':
        print "special mode stuff"
    print "do some more stuff for any mode"
    if a=='mode1':
        print "special mode stuff"

Puoi suggerire una strategia per gestirlo meglio? Aggiornerò il mio esempio se non è sufficiente per spiegare cosa sto cercando di realizzare.

    
posta Robert Lugg 31.08.2016 - 20:25
fonte

4 risposte

1

In reality, the function may be a hundred lines with these 'ifs' sprinkled through the code.

La delega viene spesso utilizzata per gestire questo tipo di situazione, in cui è necessario eseguire una serie di operazioni, ma è anche necessario fornire opportunità per personalizzare il comportamento in vari punti lungo il percorso. Definisci un'interfaccia delegata con un metodo per ognuno di quei blocchi caso speciali mode1 e sostituisci ciascuno dei blocchi con le chiamate al delegato:

def myFunc(a, b):
    if delegate != nil:
        delegate.doSpecialThing()
    print "do for any mode"
    if delegate != nil:
        delegate.doAnotherSpecialThing()

Quindi crea una classe che implementa tutti quei metodi delegati ( doSpecialThing() , doAnotherSpecialThing() , ecc.) come appropriato per il caso mode1 . Immagino che tu voglia che un singolo oggetto sia in grado di cambiarne il comportamento in modo dinamico a seconda della "modalità" in cui si trova e la delega lo rende possibile. Se disponi di altre modalità come mode2 e mode3 , puoi modificare il comportamento dell'oggetto semplicemente assegnandogli un delegato diverso o rimuoverlo del tutto per tornare al comportamento standard.

Puoi trovare molti esempi concreti nei framework Cocoa e Cocoa Touch di Apple, che utilizzano la delega ovunque per personalizzare il comportamento di determinati oggetti. Non è così comune in quei framework scambiare i delegati diversi , ma non c'è nulla che lo impedisca.

    
risposta data 31.08.2016 - 21:54
fonte
0

Il modello strategico viene utilizzato per questi tipi di problemi. In Python, probabilmente inizierei semplicemente usando i puntatori di funzione. Hai un molte opzioni su come attaccare questo in Python, ma ecco una possibile risposta:

def myFunc(a, b):
  try:
    modeFunc = locals()[a]
  except:
    modeFunc = standard

  return modeFunc(b)

e quindi avresti qualcosa di simile:

def standard(b)
  print "do for any mode"
  print "do some more stuff for any mode"

def mode1(b)
  print "special mode stuff"
  print "do for any mode"
  print "special mode stuff"
  print "do some more stuff for any mode"

Il problema ovvio qui è che hai codice comune sparpagliato in tutto. Se questo è veramente comune e deve avvenire su tutta la linea, potresti voler andare con Modello modello come sottolineato da gnat. È fondamentalmente il modello di strategia, ma forse con più struttura.

È davvero difficile dire se attenersi all'approccio semplice (e accettare qualche ridondanza) o andare con qualcosa di più strutturato. Personalmente, sceglierei quello che richiedeva meno lavoro, che tende ad essere il più semplice e quindi refactoring se si finisce con una manutenzione.

Un'altra opzione è fare entrambe le cose. Cioè, usa i puntatori di funzione e a volte possono puntare a un oggetto modello di modello o altre volte puntano semplicemente a un metodo semplice. Questa è una buona opzione se hai una logica comune con le eccezioni. Cioè, se hai condizioni come questa:

if a != "mode6":
  print "do some more stuff for any mode except mode 6"
    
risposta data 31.08.2016 - 22:27
fonte
0

Il rischio di voti bassi non reggerebbe, mi avvicinerei a questo da copia e incolla, non da modelli e astrazione.

def myFunc (a,b):
    if a == 'mode1':
        # copy of entire original function body
    else:
        # another identical copy

Ora ci sono molti rami morti dai condizionali nidificati. Quindi elimina il codice morto sotto il livello superiore if - a livello di codice se puoi, manualmente se necessario.

Ora hai esattamente un'istanza di questo rimanente condizionale e puoi vedere più facilmente il codice comune sui rami. Tagliare nuove astrazioni a questo punto, ad es. definire le funzioni locali.

Questa è una trasformazione di codice standard - sposta if sullo stack di chiamate, eliminando i rami morti nel momento in cui si verificano. Sembra un po 'strano introdurre la duplicazione prima di rimuovere la duplicazione, ma molti dello stesso ramo condizionale suggeriscono che è il modo di andare qui.

    
risposta data 31.10.2016 - 10:10
fonte
0

Risolvere questo attraverso l'ereditarietà :

Vorrei refactoring ogni if (someCondidiont) {doSomething()} in un metodo in una classe base e lasciare che l'ereditarietà sostituisca il branching:

la classe di base ha

def myFunc(a, b):
    doTask1(c,d)
    doTask2(c,d)

Mode1, Mode2, ... diventano classi derivate che implementano il doTaksX in modi diversi o non semplici.

    
risposta data 30.11.2016 - 11:51
fonte

Leggi altre domande sui tag