Parametrizzazione vs assegnazione di proprietà

7

Oggi ho avuto una discussione con un collega.

Tendo a credere che un valore in una proprietà dovrebbe essere una parte significativa dello stato di un oggetto in un dato momento. Questo automaticamente rende quasi sempre il costruttore pienamente responsabile dell'assegnazione iniziale di tutte le proprietà di una classe. Altri metodi possono successivamente modificare lo stato in un altro stato valido, ma di solito non è compito loro inizializzare i valori sulle proprietà della classe.

Il mio collega crede che le proprietà di classe possano anche essere utili per aumentare la leggibilità diminuendo il conteggio dei parametri delle funzioni private interne. Le proprietà di classe vengono quindi utilizzate come variabili temporanee, potenzialmente utilizzate da più funzioni private.

A modo mio (esempio di codice php - Ho omesso le dichiarazioni del metodo privato):

class Example {

    private $valueFromConstructor;

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

    public function doSomething() {
        $value1 = $this->computeValue1();
        $value2 = $this->computeValue2();
        $value3 = $this->computeValue3WithValue1AndValue2($value1, $value2);
        $value4 = $this->computeValue4WithValue1AndValue3($value1, $value3);

        return $this->doSomethingWithValue4($value4);
    }
}

Modo di un collega

class Example {

    private $valueFromConstructor;
    private $value1;
    private $value2;
    private $value3;
    private $value4;

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

    public function doSomething() {
        $this->computeValue1();
        $this->computeValue2();
        $this->computeValue3WithValue1AndValue2();
        $this->computeValue4WithValue1AndValue3();
        $this->doSomethingWithValue4();

        return $this->value4;
    }
}

Per me, c'è una distinzione abbastanza chiara su quando si dovrebbe assegnare un valore a una proprietà di classe e quando i valori dovrebbero essere parametrizzati. Non li vedo come alternative immediate l'una all'altra. Mi confonde vedere le variabili che vivono più a lungo dell'esecuzione del metodo a cui appartengono. La necessità che queste variabili esistano come proprietà di classe quando vengono utilizzate in metodi privati sembra creare un accoppiamento temporale.

Esistono delle linee guida su questo argomento? O è una questione di stile? E importa se l'oggetto non vive molto a lungo e ha solo pochi metodi pubblici? Per chiarire: visto dall'esterno, la classe funziona correttamente.

    
posta user2180613 24.10.2016 - 16:27
fonte

3 risposte

13

No. La tua strada è migliore.

Ecco perché: le variabili sono confinate correttamente solo allo scopo in cui vengono utilizzate. Mentre la strada del tuo collega funzionerà perfettamente, aggiungerà una dissonanza cognitiva (anche se è solo una piccola quantità ) al programmatore che viene dopo di lui, che leggerà il codice e dovrà capire perché ci sono quelle che sono le "variabili globali" all'interno della classe. Sono usati altrove? Cosa succederà se cambio uno di loro? E così via.

    
risposta data 24.10.2016 - 16:34
fonte
5

Il tuo collega è sbagliato

My colleague believes that class properties may also be useful to increase readability by decreasing the parameter count of internal private functions.

La riduzione del numero di parametri non rende necessariamente il codice più leggibile.

Quando si lavora con una funzione o un metodo, è fondamentale sapere quali sono i suoi input e output. L'approccio del tuo collega rende difficile dirlo e impossibile da raccontare dal prototipo della funzione.

Inoltre, se il codice viene eseguito su più thread, le variabili associate al membro possono essere un problema perché le istanze dell'oggetto sono conservate nello stesso heap utilizzato da tutti i thread. Questo differisce dalle variabili locali che si trovano su uno stack specifico del thread. Le variabili heap hanno il rischio di contaminazione da altri thread di esecuzione, mentre le variabili di stack sono molto più sicure.

OK, forse ha ragione, ma solo qualche volta

Detto questo, ci possono essere alcuni casi in cui funzioni molto complicate con molte variabili di lavoro / parti logiche / ricorsione / ripetizione in cui trovi che stai passando intorno a una quantità ridicola di cose. In quei casi ci sono un paio di schemi che possono essere d'aiuto, e questi potrebbero usare le variabili dei membri come preferisce il tuo collega. Qui ci sono due che posso pensare:

Oggetto metodo

Turn the method into its own object so that all the local variables become fields on that object. You can then decompose the method into other methods on the same object.

es. diciamo che devi fare uno scambio di chiavi Diffie-Helman. Questo algoritmo ha un sacco di variabili di lavoro e forse è un problema passarle ovunque, quindi le vuoi dichiarate a livello di membri.

var o = new DiffieHelmanExchanger();
o.Execute();
var result = o.SessionKey;
o.Dispose();

Note:

  • Il nome della classe finirà spesso con "-er" perché è associato ad un'azione, non ad un elemento.

  • Tutte le variabili membro vengono create solo per l'attività e poi cancellate quando l'attività è terminata.

  • C'è solo un metodo pubblico.

  • Non lo mantieni molto a lungo. Crealo, ottieni quello che vuoi, disponilo.

Conserva l'intero oggetto

Problem: You get several values from an object and then pass them as parameters to a method. Instead, try passing the whole object.

Con questo modello, gestisci un ampio set di parametri rimuovendoli dall'elenco degli argomenti e sostituendoli con un singolo "oggetto parametro". L'oggetto ha una proprietà per ciascuno dei parametri. Se la funzione deve passare i parametri a un'altra funzione, può passare lungo l'intero oggetto invece di passare i parametri uno per uno.

Questa è una sorta di ciò che il tuo collega sta sostenendo, ma invece di memorizzare le variabili di lavoro nello stesso oggetto, sono memorizzate in un oggetto dedicato alla gestione dello stato dei parametri.

Alcuni vantaggi:

  • Poiché gli oggetti vengono sempre passati per riferimento (in un certo senso), le modifiche apportate ai membri dell'oggetto parametro saranno disponibili per tutte le sottofunzioni che ricevono l'oggetto parametro come argomento.

  • Con questo modello è ancora abbastanza chiaro quali input e output siano

  • Questo modello fornisce un modo per distinguere tra variabili di lavoro (che sono memorizzate nell'oggetto parametro che viene passato) e le variabili di stato di osso fide (che sono memorizzate nell'oggetto che contiene la funzione che viene eseguita).

  • Questo modello è più resistente ai problemi di multithreading, perché ogni thread può avere la propria copia dell'oggetto parametro.

risposta data 25.10.2016 - 01:59
fonte
3

Posso dirti dall'esperienza di prima mano che il metodo del tuo collaboratore diventa un disordine irraggiungibile. Ogni volta che si ha una dipendenza da ordine di chiamata non ovvia, alla fine causerà un bug.

Supponiamo che qualcuno in seguito debba modificare doSomething () per utilizzare un altro valore:

class Example {

    private $valueFromConstructor;
    private $value1;
    private $value2;
    private $value3;
    private $value4;
    private $value5;

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

    public function doSomething() {
        $this->computeValue1();
        $this->computeValue2();
        $this->computeValue3WithValue1AndValue2();
        $this->computeValue5WithValue3AndValue4();
        $this->computeValue4WithValue1AndValue3();
        $this->doSomethingWithValue4();

        return $this->value5;
    }
}

L'errore è ovvio?

Con il tuo metodo sarebbe molto più chiaro:

class Example {

    private $valueFromConstructor;

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

    public function doSomething() {
        $value1 = $this->computeValue1();
        $value2 = $this->computeValue2();
        $value3 = $this->computeValue3WithValue1AndValue2($value1, $value2);
        $value5 = $this->computeValue5WithValue3AndValue4($value3, $value4);
        $value4 = $this->computeValue4WithValue1AndValue3($value1, $value3);

        return $this->doSomethingWithValue4($value4);
    }
}

Non ho familiarità con PHP, ma in molti linguaggi di programmazione questo genererebbe un avvertimento o un errore dovuto all'uso di $ value4 prima di dichiararlo.

O, peggio ancora, e se qualcuno lo facesse:

public function computeValue4WithValue1AndValue3()
{
    $value4 = $value1 * $value3;
    $value2 = $value4 / $value1;
}

Ora il codice diventa incredibilmente difficile da leggere e capire. Si inizia a ottenere un comportamento molto diverso in base all'ordine di chiamata di più metodi.

    
risposta data 24.10.2016 - 16:53
fonte

Leggi altre domande sui tag