Vantaggi di più metodi su Switch

12

Ho ricevuto una revisione del codice da uno sviluppatore senior che oggi mi chiede "A proposito, qual è la tua obiezione alle funzioni di dispacciamento tramite un'istruzione switch?" Ho letto in molti punti come il pompaggio di un argomento attraverso il passaggio ai metodi di chiamata sia un OOP errato, non tanto estensibile, ecc. Tuttavia, non riesco a trovare una risposta definitiva per lui. Vorrei sistemarmi tutto per me una volta per tutte.

Ecco i nostri suggerimenti sul codice in competizione (php usato come esempio, ma può essere applicato più universalmente):

class Switch {
   public function go($arg) {
      switch ($arg) {
         case "one":
            echo "one\n";
         break;
         case "two":
            echo "two\n";
         break;
         case "three":
            echo "three\n";
         break;
         default:
            throw new Exception("Unknown call: $arg");
         break;
      }
   }
}

class Oop {
   public function go_one() {
      echo "one\n";
   }
   public function go_two() {
      echo "two\n";
   }
   public function go_three() {
      echo "three\n";
   }
   public function __call($_, $__) {
      throw new Exception("Unknown call $_ with arguments: " . print_r($__, true));
   }
}

Una parte della sua argomentazione era "It (metodo switch) ha un modo molto più pulito di gestire i casi predefiniti rispetto a quello che hai nel generico metodo magico __call ()."

Non sono d'accordo riguardo alla pulizia e di fatto preferisco chiamare, ma mi piacerebbe sentire cosa hanno da dire gli altri.

Argomenti che posso presentare a sostegno dello schema Oop :

  • Un po 'più pulito in termini di codice che devi scrivere (meno, più facile da leggere, meno parole chiave da considerare)
  • Non tutte le azioni delegate a un singolo metodo. Non c'è molta differenza nell'esecuzione qui, ma almeno il testo è più compartimentato.
  • Allo stesso modo, un altro metodo può essere aggiunto ovunque nella classe invece che in un punto specifico.
  • I metodi sono spazi dei nomi, il che è bello.
  • Non si applica qui, ma considera un caso in cui Switch::go() ha operato su un membro anziché su un parametro. Dovresti prima cambiare membro, quindi chiamare il metodo. Per Oop puoi chiamare i metodi in modo indipendente in qualsiasi momento.

Argomenti che posso presentare a sostegno dello schema Switch :

  • Per ragioni, un metodo più pulito per gestire una richiesta predefinita (sconosciuta)
  • Sembra meno magico, il che potrebbe far sentire gli sviluppatori non familiari più a proprio agio

Qualcuno ha qualcosa da aggiungere per entrambe le parti? Mi piacerebbe avere una buona risposta per lui.

    
posta Explosion Pills 21.06.2011 - 15:36
fonte

5 risposte

10

Un interruttore non è considerato OOP perché spesso il polimorfismo può fare il trucco.

Nel tuo caso, un'implementazione OOP potrebbe essere questa:

class Oop 
{
  protected $goer;

  public function __construct($goer)
  {
    $this->goer = $goer;
  }

  public function go()
  {
    return $this->goer->go();
  }
}

class Goer
{
  public function go()
  {
    //...
  }
}

class GoerA extends Goer
{
  public function go()
  {
    //...
  }
}

class GoerB extends Goer
{
  public function go()
  {
    //...
  }
}

class GoerC extends Goer
{
  public function go()
  {
    //...
  }
}


$oop = new Oop(new GoerB());
$oop->go();
    
risposta data 21.06.2011 - 18:34
fonte
8

per questo esempio:

class Switch
{
    public function go($arg)
    {
        echo "$arg\n";
    }
}

OK, scherzando solo parzialmente qui. L'argomento per / contro l'uso di un'istruzione switch non può essere strongmente realizzato con un esempio così banale, perché il lato OOP dipende dalla semantica coinvolta, non solo dal meccanismo di invio .

Le dichiarazioni switch sono spesso un'indicazione di classi o classificazioni mancanti, ma non necessariamente. A volte un'istruzione switch è solo un'istruzione switch .

    
risposta data 21.06.2011 - 19:07
fonte
1

Forse non è una risposta, ma nel caso del codice non-switch, sembra che questa sarebbe una corrispondenza migliore:

class Oop {
  /**
   * User calls $oop->go('one') then this function will determine if the class has a 
   * method 'go_one' and call that. If it doesn't, then you get your error.
   * 
   * Subclasses of Oop can either overwrite the existing methods or add new ones.
   */
  public function go($arg){

    if(is_callable(array($this, 'go_'. $arg))){
      return call_user_func(array($this, 'go_'. $arg));
    }

    throw new Exception("Unknown call: $arg");
  }

  public function go_one() {
    echo "one\n";
  }
  public function go_two() {
    echo "two\n";
  }
  public function go_three() {
    echo "three\n";
  }
}

Una grande parte del puzzle da valutare è ciò che accade quando devi creare NewSwitch o NewOop . I tuoi programmatori devono saltare i cerchi con un metodo o l'altro? Cosa succede quando le tue regole cambiano, ecc.

    
risposta data 21.06.2011 - 16:01
fonte
0

Invece di mettere semplicemente in esecuzione il proprio codice di esecuzione, implementare un modello di comando completo e posizionare ciascuno di essi nella propria classe che implementa un'interfaccia comune. Questo metodo consente di utilizzare IOC / DI per cablare i diversi "casi" della classe e consente di aggiungere e rimuovere facilmente casi dal codice nel tempo. Fornisce anche un bel po 'di codice che non viola i principi di programmazione SOLID.

    
risposta data 21.06.2011 - 19:38
fonte
0

Penso che l'esempio sia cattivo. Se esiste una funzione go () che accetta l'argomento $ where, se è perfettamente valido utilizzare switch nella funzione. Avere una singola funzione go () rende più semplice modificare il comportamento di tutti go_where (). Inoltre, manterrai l'interfaccia di classe: se utilizzi vari metodi, modifichi l'interfaccia della classe con ogni nuova destinazione.

In realtà, l'opzione non deve essere sostituita con un insieme di metodi, ma con un insieme di classi: questo è il polimorfismo. Quindi la destinazione sarà gestita da ciascuna sottoclasse e ci sarà un metodo single go () per tutti. Sostituisci con il polimorfismo condizionale è uno dei refactoring di base, descritto da Martin Fowler. Ma potresti non aver bisogno del polimorfismo e cambiare è la strada da percorrere.

    
risposta data 21.06.2011 - 16:52
fonte

Leggi altre domande sui tag