"Passare oggetti anziché solo variabili richieste" è in contraddizione con "evita variabili globali"?

1

Come noto, da un lato, l'opinione principale del flusso sulla variabile globale è, è eval perché può rendere imprevedibile il programma: alcune classi o funzioni sconosciute possono cambiare il valore della variabile globale anche se non lo fanno è necessario farlo, quindi è brutto vedere questo:

public class User{ 
    public String name;
    public int age;
    public long userId;
    protected static User sharedInstance;
}

public class ShowInfoWindow{
    public ShowInfoWindow(){
        String nameString="Name:"+User.sharedInstance.name;
        String ageString="Age:"+User.sharedInstance.age;
        //UI code to display info
        .
        .
        .
    }
}

D'altro canto, il suggerimento di passare un oggetto come parametro invece della sola variabile richiesta, il motivo è:

  1. l'oggetto raggruppa insieme le proprietà correlate
  2. rende la funzione più leggibile a causa della breve lista di parametri
  3. l'aggiunta di nuove proprietà richieste nell'oggetto sarebbe molto più semplice rispetto alla modifica della firma della funzione

quindi la versione modificata dovrebbe essere:

public class ShowInfoWindow{
    public ShowInfoWindow(User user){
        String nameString="Name:"+user.name;
        String ageString="Age:"+user.age;
        //UI code to display info
        .
        .
        .
    }
}

ma secondo lo "spirito" di "evitare le variabili globali", non sta passando un oggetto anche male? Il motivo è: ShowInfoWindow non ha bisogno di accedere a user.userId, ma può farlo in realtà. Quindi, come nel caso delle variabili globali, è negativo che alcune classi o funzioni sconosciute possano cambiarlo anche se non ne hanno bisogno. Quindi passare solo la proprietà richiesta sarebbe meglio:

public ShowInfoWindow(String name,int age){
    .
    .
    .
}

È vero? Gli oggetti in transito sono in contraddizione con l'evitare variabili globali? Altrimenti, come possono le due linee guida non contraddirsi l'una con l'altra?

    
posta ggrr 25.08.2017 - 04:40
fonte

3 risposte

5

Passare oggetti non è in contraddizione con il principio di "evitare variabili globali", sono cose diverse. Tuttavia, il termine per ciò che probabilmente hai in mente è di evitare "effetti collaterali indesiderati " - sia "variabili globali" che oggetti in transito nel modo in cui hai dimostrato di avere questo in comune.

In effetti, passare oggetti mutabili da una funzione a un'altra introduce un certo rischio di ottenere tali effetti collaterali. Immagina ShowInfoWindow come una funzione di libreria chiamata da un paio di applicazioni diverse, e ora il manutentore di quella libreria (che non conosce tutte le applicazioni) cambia gli interni di ShowInfoWindow per modificare alcuni degli attributi degli oggetti user . Questo potrebbe interrompere molte di quelle applicazioni e la firma di ShowInfoWindow non impedisce questo.

Quindi come evitare questo rischio? Ci sono alcuni modi per affrontarlo. Come designer del metodo,

  • documenta esternamente che il metodo in palio non ha effetti collaterali indesiderati e scrive internamente un suggerimento per i manutentori che dovrebbe rimanere in questo modo

  • fai in modo che il metodo accetti solo i singoli parametri (come hai già suggerito nella tua domanda)

  • crea User un oggetto immutabile , quindi le funzioni chiamate non possono cambiare (almeno non facilmente) lo stato interno dell'oggetto dopo la sua creazione.

Quindi tutte queste misure dovrebbero far sì che gli utenti del metodo si fidino di esso, senza dare loro la necessità di esaminare l'implementazione.

E nel sito dei chiamanti, se uno ha a che fare con metodi o metodi non sicuri noti per effetti collaterali indesiderati, si può

  • crea una copia profonda dell'oggetto user e passa solo la copia al metodo.
risposta data 25.08.2017 - 06:46
fonte
1

but according to the "spirit" of "avoid global variables", isn't passing a object also bad?

Il passaggio di un oggetto non rende una variabile "globale". Al contrario, in effetti. La dimensione di una variabile non ha nulla a che fare con il suo ambito. Puoi creare un string o un int globale e puoi rendere un oggetto privato.

ShowInfoWindow doesn't need to access user.userId, but it can do so actually.

Sì, ma non lo farà, perché hai accuratamente scritto la funzione ShowInfoWindow() per evitare questo comportamento. Il tuo codice (e il suo comportamento relativo al parametro oggetto su cui opera) è ancora incapsulato correttamente nella tua funzione ShowInfoWindow() , quindi user.userID è ancora protetto.

Informazioni sulla regola "Passa gli oggetti anziché le singole proprietà"

Certo, perché no. È una buona idea per tutti i motivi che hai menzionato. Ma a volte passerò comunque le singole proprietà anziché l'intero oggetto, specialmente se devo solo passarne alcune e farlo renderà la funzione più facile da capire.

    
risposta data 25.08.2017 - 05:11
fonte
0

As I know, on one hand, the main stream opinion about global variable is, it is eval[sic] because it may make the program unpredictable : some unknown class or functions can change the value of the global variable even if they don't need to do so...

Questo fa parte della motivazione per evitare variabili globali, ma non è l'intera storia. È vero che le variabili globali hanno un ambito troppo ampio, così che praticamente tutto il codice nel programma ha accesso ad esse.

Tuttavia, un altro problema ha a che fare con l'istanziazione. Non possiamo controllare l'istanziazione di variabili globali tranne che (ri) avviando il programma.

Confrontiamo le funzioni connesse dallo stato globale con oggetti che hanno metodi connessi per stato oggetto.

Poiché non possiamo creare un'istanza dello stato globale, non possiamo avere più di una unità di funzionalità quando utilizziamo funzioni collegate da uno stato globale - mentre con gli oggetti, quando ne hai bisogno, lo crei; se ne hai bisogno ne crei un'altra!

Parti completamente separate del codice possono facilmente condividere il codice (implementazione) eseguendo ogni istanza del proprio oggetto secondo necessità. Usando le funzioni collegate dallo stato globale, ciascuna di queste parti separate del codice rischia di interferire con le altre.

Ciò si estende, partendo da parti separate del codice che possono essere eseguite in momenti diversi o addirittura vicini allo stesso tempo, per separare i thread. Poiché non è possibile creare un'istanza di più copie dello stato globale, lo stato globale tende a ostacolare la programmazione multi-thread, mentre uno strumento che utilizziamo per il threading sicuro consiste nell'istanziare oggetti separati per ogni thread.

Molte unità di funzionalità codificate come funzioni collegate dallo stato globale tendono a portare a spaghetti e le classi tendono a ridurre il disordine misto.

L'astrazione è uno dei nostri migliori strumenti per migliorare la manutenibilità e l'amp; longevità del codice riducendo al contempo il debito tecnico.

Raggruppando funzioni e stati insieme in oggetti incapsulati, OOP incoraggia i programmatori a concentrarsi sulla creazione di astrazioni: sulla progettazione dell'interfaccia (ad esempio l'astrazione), sulla separazione delle interfacce dall'implementazione, sul potenziale per implementazioni multiple di un'interfaccia e su la capacità di istanziare le astrazioni utilizzando varie implementazioni e, naturalmente, più istanze della stessa implementazione. Lo stato globale non incoraggia nessuno di questi.

ShowInfoWindow doesn't need to access user.userId, but it can do so actually.

Un approccio per affrontare questo è stabilire le giuste astrazioni. È possibile che tu abbia diverse astrazioni mescolate insieme, che dovrebbero essere divise in astrazioni indipendenti.

Un altro approccio consiste nell'utilizzare l'incapsulamento, come quando utilizziamo i campi di istanza private . I campi privati non sono accessibili al di fuori dell'implementazione dell'oggetto e sono quindi più stabili. Dovremmo concentrarci sull'interfaccia dell'astrazione, piuttosto che fornire l'accesso diretto ai dettagli di implementazione interna.

E l'ennesimo approccio consiste nell'utilizzare il più possibile l'immutabilità. I campi immutabili sono totalmente stabili e più facili da ragionare.

but according to the "spirit" of "avoid global variables", isn't passing a object also bad?

Non di solito. Generalmente è meno soggetto a errori e più facile da ragionare su una buona astrazione piuttosto che su int e stringhe abbinate. Una buona astrazione lega insieme int e string in un unico concetto, e questa associazione è più difficile da mescolare e abbinare accidentalmente.

Considera un codice che ha a che fare con due cose invece di una. Ad esempio, codifica & decodificare. Se i clienti devono trattare separatamente ciascuna di queste implementazioni come una coppia, è possibile utilizzare accidentalmente il decodificatore sbagliato per una codifica o viceversa. Considerando che se presentiamo una singola astrazione che lega insieme una codifica & decodificare, il client (programmatore che consuma) non mescolerà in modo inappropriato le implementazioni.

Questo potrebbe applicarsi a una situazione che coinvolge più utenti se, per esempio, è necessario un codice necessario per gestire due utenti allo stesso tempo. Se il client delle astrazioni utilizza le variabili locali name1 e age1 , insieme a name2 e age2 , non ci sarebbe nessun errore (tipo sistema) fornito accoppiando accidentalmente name1 con age2 ; l'associazione viene mantenuta implicitamente dal programmatore client - mentre una volta che a name e age sono vincolati in un'astrazione user , l'accoppiamento è esplicito e non c'è possibilità per questo mix accidentale.

    
risposta data 25.08.2017 - 18:00
fonte

Leggi altre domande sui tag