È una cattiva pratica risolvere gli argomenti nulli alle variabili statiche predefinite?

4

Per prima cosa, lascia che ti mostri un esempio (scritto in ActionScript 3.0):

class GameObject {
    public static function MakeFromName( pName:String,
                                         pAtlas:TextureAtlas = null,
                                         pGameData:Object = null ):GameObject {

        // If these arguments are not passed, they default to the static INSTANCE's 
        // currentAtlas and currentData.
        if (pAtlas == null) pAtlas = GameBaseClass.INSTANCE.currentAtlas; // <--
        if (pGameData == null) pGameData = GameBaseClass.INSTANCE.currentData; // <--

        var theSymbolData:Object = pGameData.symbols[pName];
        var theSymbolTextures:Vector.<Texture> = pAtlas.getTextures( pName );

        var newGO:GameObject = new GameObject();
        newGO.MakeFromData( pName, theSymbolTextures, theSymbolData );

        return newGO;
    }

    // MakeFromData defined somewhere down in the code...
}

Sto avendo questo dibattito con un collega per quanto riguarda i precedenti parametri "null" (fondamentalmente facoltativi).

Per me, ha senso risolvere automaticamente quei due parametri ( pAtlas e pGameData ) in una sorta di risorsa centrale (in questo caso, l'Atlante primario e Dati utilizzati nel gioco). Questo GameBaseClass va di pari passo con GameObject , quindi non vedo alcun danno nel riferirmi alle sue proprietà di istanza di singleton. Lo sviluppatore ha ancora la possibilità di fornire il proprio Atlas e dati.

MA, il mio collega crede che questo leghi le classi troppo insieme - non è abbastanza sciolto. Posso capire il suo punto se si riferisse effettivamente alle classi derivate usate nel gioco (cioè: AwesomeGame.INSTANCE.currentAtlas , dove AwesomeGame estende GameBaseClass ). Ma non lo è! Dal suo punto di vista, lo sviluppatore dovrebbe essere costretto a inserire ogni singolo parametro, nulla di opzionale (in questa particolare situazione).

C'è un modo per avere il meglio di entrambi i mondi?

L'unico altro modo in cui posso pensare è solo scrivere due metodi separati (uno con il 2 ° e il 3 ° argomento e uno senza), ma che comunque non risolve il problema con la dipendenza GameBaseClass.

Qualche idea?

EDIT: Nell'esempio precedente, ho utilizzato ActionScript 3.0 che non supporta l'assegnazione di variabili non costanti. In altre parole, consente numeri hardcoded, stringhe (anche vuote), booleane e costanti (anche se non penso che funzionerebbe con costanti non primitive, potrebbe comunque essere sbagliato). Poiché GameBaseClass.INSTANCE.currentAtlas è una proprietà che potrebbe cambiare nel corso della durata dell'applicazione in esecuzione, non può essere indicata come valore di default 'compile-time'. Spero che abbia più senso!

    
posta bigp 07.08.2014 - 17:20
fonte

6 risposte

6

Va perfettamente bene. Questo è spesso il compromesso tra "passare tutto dentro rende troppo complesso!" e "usare una statica rende le cose difficili e difficili da testare!" campi.

Personalmente, lo farei solo se l'utilizzo comune (75% +) è di utilizzare l'istanza predefinita. Altrimenti, le persone andranno con la via più semplice (meno argomenti) che non è necessariamente la rotta corretta . Far sì che le persone specifichino i parametri significa che diranno "come faccio a fare un TextureAtlas ?" piuttosto che usare quello comune, che è il suo percorso errato.

Avere questi valori predefiniti incoraggi gli utenti della classe a fare la cosa giusta la maggior parte del tempo, senza impedir loro di fare la cosa giusta quando si discosta dalla norma (con un po 'di lavoro in più).

    
risposta data 07.08.2014 - 17:41
fonte
3
risposta data 07.08.2014 - 17:39
fonte
2

Sì, è una pessima pratica, per diversi motivi, incluso almeno:

  1. Lo stato globale non è valido in generale. Avendo un codice che usa solo lo stato globale quando l'argomento è nullo, si consente di essere usato correttamente e di essere testato, ma qualsiasi altro codice che usa il proprio codice con un argomento nullo diventa problematico, come eredita questo stato globale. Se hai due o più componenti che utilizzano il tuo codice con argomento nullo e poi decidi di poterli utilizzare contemporaneamente, sei nei guai.

  2. È soggetto a errori. Se un chiamante passa accidentalmente nulla, forse come risultato di un errore logico nel chiamante (spesso sotto forma di una condizione di errore non gestita), il codice agisce su un oggetto non correlato anziché produrre un errore.

  3. È costato. Forse a basso costo, ma ancora a costo, sotto forma di (1) un ramo extra per testare null e fare qualcosa di speciale, e (2) il costo dell'oggetto / stato extra che i chiamanti ben scritti non useranno mai, ma che deve esistere comunque in fase di esecuzione perché potrebbe essere referenziato.

risposta data 07.08.2014 - 21:42
fonte
2

Una parte: il valore predefinito dei parametri è impostato su null e quindi la prima cosa che fai è null controllarli. Hai provato a sovraccaricare il metodo. Vale a dire

public static function MakeFromName(String mystring)
{
    MakeFromName(mystring, GameBaseClass.INSTANCE.currentAtlas, GameBaseClass.INSTANCE.currentData)
}
    
risposta data 07.08.2014 - 22:24
fonte
0

Non credo che i tuoi valori predefiniti creino l'accoppiamento. Penso che volerlo sia un segno che l'accoppiamento esiste già. Ci sono almeno altri due segni di accoppiamento stretto qui:

  • Non stai nemmeno usando questi parametri direttamente, stai usando membri di quei parametri, che è una Legge di Demeter violazione.
  • In cerca di più metodi tipo fabbrica che si chiamano a vicenda.

Ci sono altri modi per evitare di passare i parametri ovunque:

  • Sposta MakeFromName in un'altra classe esistente. Forse il codice che lo chiama o TextureAtlas .
  • Crea una nuova classe al solo scopo di creare GameObject s. Il suo costruttore potrebbe memorizzare Atlas e GameData , quindi puoi creare molti GameObject s con le stesse dipendenze con factory.MakeFromName(name) .
  • Inverti le dipendenze in modo che GameObject non debba conoscere le trame e i simboli.
  • Passa successivamente alle trame e ai simboli quando GameObject è usato , anziché quando viene creato.
  • Sposta la responsabilità per i valori predefiniti nel costruttore GameObject . In altre parole, hanno simboli e trame predefiniti invece di atlanti e dati di gioco predefiniti.
  • Consolida i luoghi in cui chiama MakeFromName in un unico posto. Se hai un luogo in cui viene creata l'associazione tra atlante, dati di gioco e nome, invece di essere sparsi ovunque, non ti importa di un paio di parametri extra.

Non posso davvero consigliarti ulteriormente senza vedere tutto il tuo codice. Tieni presente che spesso è difficile dire se questi suggerimenti ti saranno utili senza averli provati.

Come nota a margine, mi chiedo il motivo per cui non hai reso questi oggetti come predefiniti nella firma della funzione, invece di assegnarli a null e controllare null successivamente. Sembra un passaggio intermedio non necessario.

Inoltre, come nota a margine, non so quale lingua sia, ma suppongo che Object sia la sua classe base di livello superiore dalla quale tutti gli oggetti alla fine derivano. In tal caso, non è consigliabile utilizzarlo in questo modo per i dati di gioco.

    
risposta data 07.08.2014 - 20:54
fonte
0

Non potresti fare qualcosa del genere:

class GameObject {
    public static function MakeFromName( pName:String,
                                         pAtlas:TextureAtlas = GameBaseClass.INSTANCE.currentAtlas,
                                         pGameData:Object = GameBaseClass.INSTANCE.currentData ):GameObject {

        var theSymbolData:Object = pGameData.symbols[pName];
        var theSymbolTextures:Vector.<Texture> = pAtlas.getTextures( pName );

        var newGO:GameObject = new GameObject();
        newGO.MakeFromData( pName, theSymbolTextures, theSymbolData );

        return newGO;
    }

    // MakeFromData defined somewhere down in the code...
}

In questo modo le tue intenzioni diventano chiare dalla firma, se non passerai il valore, tornerà al valore predefinito.

    
risposta data 07.08.2014 - 21:01
fonte

Leggi altre domande sui tag