"call and return" è un pattern o un antipattern?

2

Immagina di avere questo codice:

class Foo:
  def __init__(self, active):
    self.active = active
  def doAction(self):
    if not self.active:
      return
      # do something

f=Foo(false)
f.doAction() # does nothing

Questo è un bel codice; In realtà ho (non in Python) una variabile attiva globale chiamata "dosomething" e una routine chiamata "qualcosa", dove la prima cosa che accade all'interno della routine "qualcosa" è if not dosomething return .

Un'implementazione alternativa chiamerebbe una routine che esegue sempre un'azione, e il flag è controllato al momento dell'invocazione, come nel seguente codice:

class Foo:
  def doAction(self):
    # do something

doaction = False

f=Foo()
if doaction:
  f.doAction()

Qual è la tua opinione su questo? Personalmente, trovo che la prima soluzione viola il principio meno sorprendente: il chiamante sta invocando un'azione che non viene mai eseguita in risposta a uno stato che è stato impostato da qualche altra parte, ma dal codice sembra che l'azione venga eseguita.

Lo considereresti un modello totale, un antipattern totale o solo un'opzione senza una strong opinione a favore o contro di essa?

    
posta Stefano Borini 26.01.2011 - 17:49
fonte

6 risposte

7

Credo che entrambi abbiano il loro posto; a seconda di dove deve risiedere la logica on / off.

Vorrei utilizzare il primo caso più incapsulato se il controllo deve essere locale a Foo, ad es. per esempio se fosse uno stato di connessione per i componenti interni di Foo.

Il secondo caso controllo esterno ha più senso se il chiamante conosce correttamente lo stato di doAction . Se il chiamante era di classe Bar e qualche volta volevi usare Foo per qualche scopo e talvolta fare qualcos'altro, forse.

    
risposta data 26.01.2011 - 18:13
fonte
4

Non direi dogmaticamente che questo è un modello o un antipattern. Penso che questo modello abbia un posto nel codice "buono". Dipende dal contesto. Ad esempio, non vorrei vedere questo tipo di cose in un metodo chiamato WriteToDatabase (). Quando chiamo quel metodo, mi aspetto che qualcosa venga scritto nel database. Tuttavia, se esistesse un metodo chiamato OpenDatabaseConnection (), sarei a posto con quel metodo che non sta facendo nulla se la connessione è già stata aperta. In questo modo chiamanti diversi o thread diversi o qualsiasi altra cosa può sempre chiamarlo solo per essere sicuri, ma non devono controllare "apertura" prima di effettuare la chiamata.

    
risposta data 26.01.2011 - 19:45
fonte
2

Il tuo primo esempio è OO valido e potrebbe essere usato per molte cose come essere sicuro che una connessione sia aperta, ecc. Non sono sicuro che abbia un nome.

Il secondo esempio è ok. Non è proprio un modello nella mia mente, solo un controllo delle condizioni prima di eseguire un'azione. (Beh, immagino che Sequence, Selection, Iteration sia un pattern.)

Entrambi i metodi sono utili in diverse circostanze.

    
risposta data 26.01.2011 - 17:59
fonte
1

Il principio pertinente è secondo me: Tell, Do not Ask

La domanda è: è self.active qualcosa all'interno del dominio Foo .

Se è nel dominio di Foo , e tu vai per la seconda soluzione, violerei l'incapsulamento imponendo la sua conoscenza al chiamante e dovresti evitare. Un buon esempio potrebbe essere un cliente, che è considerato attivo quando ha pagato i suoi conti.

Se non è nel dominio di Foo , si dovrebbe refactoring, in modo che active non sia una variabile membro di Foo .

    
risposta data 12.09.2011 - 15:44
fonte
0

Credo che questo sia uno schema utile, a condizione che tu abbia un'asserzione (o un messaggio di log di debug) prima che ritorni. Quando l'applicazione viene distribuita, l'asserzione dovrebbe essere disattivata e quindi il metodo dovrebbe restituire in modo innocuo senza crash. D'altra parte dovresti vederlo durante lo sviluppo e puoi refactoring il tuo codice in modo che la chiamata non venga fatta in primo luogo.

    
risposta data 19.12.2011 - 07:13
fonte
0

Mentre di solito vedo call e return usato nel codice del mio collega (e mio), non lo raccomanderei in questo caso (lo usiamo per implementare proprietà fastfail) . Se possibile, prova ad applicare il modello di stato .

In questo caso, attivo e inattivo sarebbero stati validi su cui l'implementazione di doAction(...) cambia. Questo dovrebbe chiarire il codice e renderlo più gestibile. Inoltre, ottieni il vantaggio delle transizioni di stato, ad esempio informObservers(boolean active) .

    
risposta data 19.12.2011 - 08:52
fonte