Perché abbiamo bisogno di variabili private?

202

Perché abbiamo bisogno di variabili private nelle classi?

Ogni libro sulla programmazione che ho letto dice che questa è una variabile privata, è così che la definisci ma si ferma lì.

La formulazione di queste spiegazioni mi è sempre sembrata come se avessimo davvero una crisi di fiducia nella nostra professione. Le spiegazioni hanno sempre suonato come se altri programmatori volessero rovinare il nostro codice. Tuttavia, ci sono molti linguaggi di programmazione che non hanno variabili private.

  1. Che cosa impediscono le variabili private?

  2. Come decidi se una determinata proprietà dovrebbe essere privata o non? Se per impostazione predefinita ogni campo DOVREBBE essere privato allora perché ci sono membri di dati pubblici in una classe?

  3. In quali circostanze deve essere resa pubblica una variabile?

posta mwallace 10.04.2012 - 07:31
fonte

14 risposte

295

Non è tanto una questione di fiducia, ma piuttosto di gestione della complessità.

È possibile accedere a un membro pubblico al di fuori della classe, che per considerazioni pratiche significa "potenzialmente ovunque". Se qualcosa va storto in un campo pubblico, il colpevole può essere ovunque, e quindi per rintracciare il bug, potresti dover guardare un bel po 'di codice.

Un membro privato, al contrario, è accessibile solo dalla stessa classe, quindi se qualcosa va storto, di solito c'è solo un file sorgente da guardare. Se hai un milione di linee di codice nel tuo progetto, ma le tue classi sono ridotte, questo può ridurre lo sforzo di tracciamento dei bug di un fattore di 1000.

Un altro vantaggio è legato al concetto di "accoppiamento". Un membro pubblico m di una classe A utilizzato da un'altra classe B introduce una dipendenza: se si modifica m in A , è necessario controllare anche gli usi di m in B . Peggio ancora, niente in classe A ti dice dove viene utilizzato m , quindi di nuovo devi cercare nell'intero codebase; se è una libreria che stai scrivendo, devi anche assicurarti che il codice al di fuori del tuo progetto non si interrompa a causa della tua modifica. In pratica, le librerie tendono ad attenersi con le loro firme dei metodi originali il più a lungo possibile, non importa quanto siano dolorose, e quindi introducono un blocco di modifiche di rottura con un importante aggiornamento della versione. Con i membri privati, al contrario, puoi escludere le dipendenze immediatamente: non è possibile accedervi dall'esterno, quindi tutte le dipendenze sono contenute nella classe.

In questo contesto, "altri programmatori" includono i tuoi sé futuri e passati. Probabilmente sai ora che non dovresti fare questa cosa X con la tua variabile Y, ma sei destinato ad aver dimenticato tre mesi lungo la strada quando un cliente ha urgente bisogno di implementare alcune funzionalità, e ti chiedi perché fare X rompe Y in modi oscuri.

Quindi, quando dovresti rendere le cose private: direi rendere tutto privato di default, e quindi esporre solo quelle parti che devono essere assolutamente pubbliche. Più puoi rendere privato, meglio è.

    
risposta data 10.04.2012 - 07:48
fonte
110

Le variabili private aiutano a impedire alle persone di dipendere da determinate parti del codice. Ad esempio, supponi di voler implementare una struttura dati. Vuoi che gli utenti della tua struttura dati non si preoccupino di come l'hai implementato, ma piuttosto di utilizzare l'implementazione attraverso l'interfaccia ben definita. Il motivo è che se nessuno dipende dalla tua implementazione, puoi cambiarlo quando vuoi. Ad esempio, è possibile modificare l'implementazione back-end per migliorare le prestazioni. Tutti gli altri sviluppatori che dipendono dalle implementazioni si interromperanno, mentre gli utenti dell'interfaccia andranno bene. Avere la flessibilità di cambiare le implementazioni senza influenzare gli utenti della classe è un enorme vantaggio che utilizza variabili private (e più in generale, incapsulamento ).

Inoltre, non è davvero una "crisi di fiducia". Se rendi pubblico un dato, non puoi assicurarti che nessuno ne dipenda. Spesso è molto conveniente dipendere da alcune variabili specifiche dell'implementazione, anziché passare attraverso l'interfaccia pubblica, specialmente nel calore di una scadenza. Inoltre, gli sviluppatori non si renderanno conto sempre che dipendono da qualcosa che potrebbe cambiare.

Quindi questo tipo di risposte alle tue altre domande, spero. Tutti i dettagli dell'implementazione devono essere privati e la parte pubblica dovrebbe essere un'interfaccia piccola, concisa e ben definita per l'utilizzo della classe.

    
risposta data 10.04.2012 - 07:41
fonte
24

La parola chiave qui è Incapsulamento . In OOP si desidera utilizzare variabili private per forzare l'incapsulamento corretto degli oggetti / delle classi.

Mentre altri programmatori non sono pronti a prenderti, interagiscono con il tuo codice. Se non si rende privata una variabile, potrebbe farvi riferimento nel proprio codice senza voler fare alcun danno. Tuttavia, se hai bisogno di tornare in classe e cambiare qualcosa, non sai più chi usa quale variabile e dove. L'obiettivo dell'incapsulamento è rendere esplicita l'interfaccia esterna della classe in modo che tu sappia che solo questi (in genere) metodi potrebbero essere stati usati da altri.

  1. Quindi, le variabili private assicurano che la variabile corrispondente rimanga solo nella classe di definizione. Se hai bisogno di cambiarlo, la modifica è locale alla classe stessa.

  2. Nei lanugages tradizionali come C ++ o Java, di solito rendi tutto privato e accessibile solo dai getter e setter corrispondenti. Non c'è bisogno di decidere molto.

  3. A volte, es. in una struttura C ++, hai bisogno di una classe solo come un modo per raggruppare più cose insieme. Ad esempio, considera una classe Vector che ha solo un attributo x e un y . In questi casi, puoi consentire l'accesso diretto a questi attributi dichiarandoli pubblici. In particolare, non ti importa se un codice esterno modifica un oggetto della tua classe scrivendo direttamente nuovi valori in x o y .

Come punto aggiuntivo, nota che questo argomento è considerato leggermente diverso in altre lingue. Ad esempio, i linguaggi strongmente radicati nella programmazione funzionale enfatizzano l'immutabilità dei dati, vale a dire che i valori dei dati non possono essere modificati affatto. In questi casi, non devi preoccuparti di ciò che altri programmatori (o piuttosto il loro codice) fanno ai tuoi dati. Semplicemente non possono fare nulla che possa influenzare il tuo codice, perché tutti i dati sono immutabili.

In queste lingue, ottieni quindi qualcosa chiamato principio di accesso uniforme , dove deliberatamente non si distinguono metodi come getter e setter, ma fornisce l'accesso diretto alla variabile / funzione. Quindi potresti dire che le dichiarazioni private sono molto più popolari negli scenari orientati agli oggetti.

Questo dimostra anche come ampliare le tue conoscenze per includere altri campi ti consente di visualizzare i concetti esistenti in un modo completamente nuovo.

    
risposta data 10.04.2012 - 07:48
fonte
6

Le variabili private assicurano che i riferimenti successivi al di fuori dell'ambito di un oggetto o funzione non influenzeranno inavvertitamente altre variabili. Per i progetti di programmazione di grandi dimensioni, questo può aiutare a evitare molti problemi interessanti (che di solito non sono presi dal compilatore).

Ad esempio, linguaggi prototipali come Javascript possono consentire agli utenti di intonacare le variabili come meglio credono:

function SomeObject() {
    this.a = 2; //Public (well, actually protected) variable

    this.showA = function() {
        alert(this.a);
    }
}

//Some lines down...

var obj = new SomeObject();
obj.a = 3;
obj.showA(); //Will alert 3. Uh oh!

Se quella variabile era privata, non sarebbe possibile modificarla dall'esterno:

function SomeObject() {
    var a = 2; //Private variable

    this.showA = function() {
        alert(a);
    }
}

//Some lines down...

var obj = new SomeObject();
obj.a = 3;
obj.showA(); //Will alert 2

Detto questo, non tutte le variabili devono essere private e l'uso eccessivo di variabili private può effettivamente rendere il codice più ingombrante. I campi banali che non incidono seriamente su un oggetto non hanno bisogno dei metodi get() e set() .

Le variabili private semplificano anche l'espansione del codice in futuro, senza fare affidamento su una documentazione pesante su ciò che fanno le molte variabili pubbliche. C'è meno di una minaccia di interruzione accidentale delle funzionalità se è possibile sovrascrivere meno variabili.

Le variabili pubbliche dovrebbero essere utilizzate quando un oggetto / funzione accederà ad altri oggetti / funzioni, poiché di norma le variabili private non possono farlo. Potresti avere da poche a diverse variabili pubbliche, e non è una cattiva idea usarle se vengono utilizzate correttamente.

    
risposta data 10.04.2012 - 07:52
fonte
6

Ci sono alcune buone risposte che citano i benefici per i futuri manutentori e utenti di una classe. Tuttavia, ci sono anche benefici per il design originale. Fornisce un chiaro punto di demarcazione tra quando il tuo obiettivo è rendere le cose più facili a te stesso e quando il tuo obiettivo è rendere le cose più facili all'utente della classe. Quando scegli tra pubblico e privato, segnala al tuo cervello di passare a una modalità o l'altra e ottieni un'API migliore.

Non è che le lingue senza privacy rendano impossibile una progettazione del genere, solo meno probabile. Invece di essere spinti a scrivere qualcosa come foo.getBar() , le persone sono inclini a pensare che un getter non sia necessario, perché è più facile scrivere foo.bar , ma poi quella decisione non viene rivisitata quando più tardi finirai con mostruosità più lunghe come %codice%. I programmatori che non hanno mai lavorato in un linguaggio più rigoroso potrebbero anche non vedere cosa c'è che non va.

Ecco perché nelle lingue senza privacy le persone adottano spesso standard di denominazione che lo simulano, come la preposizione di un trattino basso a tutti i membri "privati". È un segnale utile anche quando la lingua non lo impone.

    
risposta data 10.04.2012 - 17:53
fonte
3

Tutte le variabili dovrebbero essere private a meno che non debbano assolutamente essere pubbliche (che non è quasi mai, dovresti usare proprietà / getter e setter).

Le variabili forniscono in gran parte lo stato dell'oggetto e le variabili private impediscono ad altri di entrare e cambiano lo stato dell'oggetto.

    
risposta data 10.04.2012 - 07:38
fonte
2
  • Che cosa impediscono le variabili private?

Si tratta di chiarire quali proprietà e metodi sono l'interfaccia e quali sono in realtà funzionalità principali. Metodi / proprietà pubbliche riguardano spesso altro codice di altri sviluppatori che utilizzano i tuoi oggetti. Non ho mai lavorato a squadre di 100+ sullo stesso progetto, ma nella mia esperienza di forse team di 3-5 con altri 20 sviluppatori che usano roba che ho scritto, le altre preoccupazioni sembrano sciocche.

Nota: sono principalmente uno sviluppatore JavaScript. Generalmente non mi preoccupo per gli altri sviluppatori che visualizzano il mio codice, e sono pienamente consapevole che potrebbero semplicemente ridefinire le mie cose in qualsiasi momento. Presumo che siano abbastanza competenti per sapere cosa stanno facendo prima. In caso contrario, è improbabile che lavorerei su un team che non usa il controllo del codice sorgente.

  • Come decidi se un determinato insieme di proprietà dovrebbe essere privato o no? Se per impostazione predefinita ogni campo DOVREBBE essere privato, allora perché ci sono membri di dati pubblici in una classe?

Ero abituato a pensare che fosse sciocco mettere getter e setter su proprietà private quando non si stava realmente effettuando alcun tipo di convalida o altro trattamento speciale della proprietà da impostare. Continuo a non pensare che sia sempre necessario, ma quando si ha a che fare con una grande scala, una complessità elevata, ma soprattutto molti altri sviluppatori che si affidano agli oggetti che si comportano in modo coerente, può essere utile fare le cose in un modo coerente e uniforme. Quando le persone vedono i metodi chiamati getThat e setThat , sanno esattamente quali sono le intenzioni e possono scrivere i loro oggetti per interagire con i tuoi con l'aspettativa che otterranno sempre pomodori piuttosto che tomahtos. Se non lo fanno, sanno che il tuo oggetto sta dando loro qualcosa che probabilmente non dovrebbe, il che significa che sta permettendo ad altri oggetti di fare qualcosa con quei dati che probabilmente non dovrebbe. Puoi controllarlo quando se ne presenta la necessità.

L'altro grande vantaggio è che è più semplice trasferire gli oggetti o interpretare i valori in modo diverso da un getter o setter basato su un tipo di contesto di stato variabile. Poiché stanno accedendo alle tue cose con un metodo, è molto più semplice cambiare il modo in cui il tuo oggetto opera in un secondo momento senza rompere il codice che dipende da esso. Il principio importante è che solo il tuo oggetto modifica effettivamente i dati per i quali l'hai reso responsabile. Questo è particolarmente importante per mantenere il codice liberamente accoppiato che è una grande vittoria in termini di portabilità e facilità di modifica.

  • In quali circostanze deve essere resa pubblica una variabile?

Con le funzioni di prima classe (puoi passare le funzioni come argomenti), non riesco a pensare a molti validi motivi se non che non sia assolutamente necessario farlo su progetti di dimensioni minori. Senza di esso, suppongo che potresti avere membri che vengono elaborati pesantemente e regolarmente da altri oggetti nella misura in cui sembra un po 'crufoso chiamare costantemente get e set su un oggetto che non tocca realmente quei dati stessi, ma che in stesso mi farebbe chiedere perché l'oggetto fosse responsabile di quei dati. In generale, tenderei a voler riesaminare la mia architettura alla ricerca di difetti prima di decidere se una proprietà pubblica dei dati è necessaria. IMO, non c'è niente di sbagliato in una lingua che ti permette di fare qualcosa che di solito è una cattiva idea. Alcune lingue non sono d'accordo.

    
risposta data 10.04.2012 - 18:47
fonte
2

Vedi questo post di una domanda correlata su SO .

In breve, l'ambito variabile ti consente di mostrare agli utenti del tuo codice ciò che dovrebbero e non dovrebbero fare scherzi. Una variabile privata può contenere dati che sono stati "controllati" utilizzando un setter di proprietà o un metodo di elaborazione per garantire che l'intero oggetto sia in uno stato coerente. La modifica diretta del valore della variabile privata può causare l'incoerenza dell'oggetto. Rendendolo privato, qualcuno deve lavorare molto duramente e avere permessi di runtime molto alti, per poterlo cambiare.

L'ambito dell'aspetto del membro è quindi la chiave del codice di autocertificazione.

    
risposta data 10.04.2012 - 16:42
fonte
2

Non ha nulla a che fare con la fiducia o la paura degli attacchi, si tratta esclusivamente di incapsulamento - non costringendo le informazioni non necessarie all'utente della classe.

Considera costanti private - non devono contenere valori segreti (quelli dovrebbero essere memorizzati altrove), non possono essere modificati, non devono essere passati nella tua classe (altrimenti dovrebbero essere pubblici). L'unico uso possibile per loro è come costanti in ALTRE classi. Ma se lo fai, quelle classi ora dipendono dalla tua classe, per fare un lavoro che non è correlato alla tua classe. Se cambi la costante, le altre classi potrebbero rompersi. Non va bene da entrambe le parti: come scrittore della tua classe, vuoi la libertà di cambiare il più possibile, e non vuoi preoccuparti di cose al di fuori del tuo controllo. Un consumatore della tua classe vuole essere in grado di dipendere dai dettagli esposti della tua classe, senza preoccuparsi di cambiarlo e rompere il codice.

Un consumatore della tua classe vuole sapere tutto ciò che è necessario per INTERAGGERE con la tua classe, e non vuole sapere nulla della tua classe che non cambi il modo in cui lo fa: è inutile trivia. Se hai usato un langauge con la riflessione, quanto spesso lo hai usato per imparare non come alcune classi fanno qualcosa o dove sta lanciando un'eccezione in modo imprevisto, ma semplicemente il nome dei campi e dei metodi privati? Sto scommettendo mai. Perché non hai alcuna utilità per quella conoscenza.

    
risposta data 10.04.2012 - 17:41
fonte
1

Il concetto OOP ha ereditarietà ha una delle sue caratteristiche (Java o C ++). Quindi, se vogliamo ereditare (nel senso che accediamo alle variabili della classe ereditata), c'è la possibilità di influenzare tali variabili. Quindi dobbiamo decidere se le variabili possono essere cambiate o meno.

Solo a questo scopo, stiamo usando i modificatori di accesso in OOP. Uno dei modificatori è privato, il che significa che è accessibile solo da quella classe. Qualsiasi altra classe non può influenzare tali variabili.

Come sappiamo, protetto significa che è possibile accedere alla classe che verrà ereditata.

Perché c'è un concetto di modificatore in OOP, è dovuto a questi motivi (qualsiasi classe può accedere ad altre variabili di classe usando il concetto OOP). Se non esiste un concetto di modificatore, significa che sarebbe stato difficile mentre si ereditano le classi o si utilizzano altri concetti di OOP.

    
risposta data 10.04.2012 - 07:52
fonte
1

Parlerò del valore dell'incapsulamento da una prospettiva diversa usando un esempio reale. Un po 'di tempo fa (all'inizio degli anni '80) fu scritto un gioco per il computer a colori Radio Shack chiamato Dungeons of Daggorath. Alcuni anni fa, Richard Hunerlach lo portò da un assemblatore di carta che elencava in C / C ++ ( link ). Qualche tempo dopo, ho ottenuto il codice e ho iniziato a ridimensionarlo per fornire una migliore incapsulamento, ( link ).

Il codice era già scomposto in oggetti diversi per gestire la pianificazione, il video, l'input dell'utente, la creazione di dungeon, i mostri, ecc. ma si poteva sicuramente dire che era stato portato dall'assemblatore. Il mio più grande compito era aumentare l'incapsulamento. Con ciò intendo ottenere parti diverse del codice per togliersi il naso dagli affari degli altri. C'erano molti posti, ad esempio, che avrebbero cambiato le variabili video direttamente per avere un qualche effetto. Alcuni lo hanno fatto con il controllo degli errori, altri no, altri avevano idee leggermente diverse su cosa significasse cambiare quella cosa. C'era molta duplicazione del codice. Invece, la sezione video doveva avere un'interfaccia per svolgere le attività desiderate, e il codice per farlo poteva essere tutto in un posto, un'idea, un posto per il debug. Quel genere di cose era dilagante. Ogni oggetto a cui si accede direttamente ogni altro oggetto e codice è stato duplicato ovunque.

Mentre il codice iniziava a essere preso in giro, i bug che non erano nemmeno stati diagnosticati andarono via. Il codice è diventato di qualità superiore. Ogni compito è diventato responsabilità di un posto nel codice e c'era solo un posto per farlo bene. (Ne trovo ancora ogni volta che faccio un altro passaggio attraverso il codice.)

Tutto ciò che è visibile sul tuo codice è la tua interfaccia. Che tu lo voglia o no. Se limiti la visibilità il più possibile, non sarai tentato di inserire il codice nel posto sbagliato più tardi. Rende ovvio quale parte del codice sia responsabile di quali dati. Rende il design migliore, più pulito, più semplice, più elegante.

Se rendi un oggetto responsabile per i propri dati e fornisci interfacce a tutti gli altri che hanno bisogno di qualcosa, il codice diventa MOLTO più semplice. Cade fuori Hai piccole routine semplici che fanno solo una cosa. Meno complessità == meno bug.

    
risposta data 17.04.2012 - 21:27
fonte
1

Come affermato da altri, le variabili private sono buone per evitare errori di utilizzo che portano l'oggetto in uno stato incoerente e difficili da tracciare bug ed eccezioni impreviste.

Ma d'altra parte, ciò che è stato per lo più ignorato dagli altri riguarda i campi protetti.

Una sottoclasse estesa avrà pieno accesso ai campi protetti, rendendo l'oggetto fragile come se tali campi fossero pubblici, ma la fragilità è limitata alla classe estendentesi (a meno che non esponga tali campi ancora di più).

Quindi, i campi pubblici sono difficili da considerare buoni, e alla data l'unica ragione per usarli è per le classi usate come parametri di configurazione (una classe molto semplice con molti campi e nessuna logica, così che la classe viene passata come parametro solo per qualche metodo).

Ma d'altra parte, i campi privati riducono la flessibilità del tuo codice agli altri utenti.

Flessibilità contro problemi, pro e contro:

Gli oggetti istanziati dal tuo codice nella classe vanilla con campi protetti sono al sicuro e sono di tua esclusiva responsabilità.

D'altra parte, gli oggetti che estendono la tua classe con campi protetti, istanziati dagli utenti del tuo codice, sono di loro competenza, non i tuoi.

Quindi, campi / metodi protetti non ben documentati, o se gli utenti non capiscono veramente come dovrebbero essere usati tali campi e metodi, hanno buone probabilità di causare problemi inutili a loro stessi e a te.

D'altra parte, rendere privata la maggior parte delle cose ridurrà la flessibilità degli utenti, e potrebbe anche metterli alla ricerca di alternative mantenute, poiché potrebbero non voler creare e mantenere un fork solo per fare in modo che le cose accadano.

Quindi, un buon equilibrio tra privato, protetto e pubblico è ciò che conta davvero.

Ora, decidere tra privato e protetto è il vero problema.

Quando utilizzare protetto?

Ogni volta che capisci che un campo può essere estremamente flessibile, dovrebbe essere codificato come protetto. Questa flessibilità è: da diventare nullo (dove nullo è sempre controllato e riconosciuto come uno stato valido che non genera eccezioni), ad avere vincoli prima di essere usato dalla classe ex. > = 0, < 100 ecc. E riparato automaticamente per valori sovra / sotto-flussi, generando al massimo un messaggio di avviso.

Quindi, per tale campo protetto potresti creare un getter e usarlo solo (invece di usare direttamente la variabile di campo), mentre altri utenti potrebbero non usarlo, nel caso volessero una maggiore flessibilità al loro codice specifico, nel mio esempio potrebbe essere come: se vogliono che i valori negativi funzionino bene nella loro classe estesa.

    
risposta data 17.06.2016 - 04:48
fonte
0

imo @tdammers non è corretto ed è in realtà fuorviante.

Esistono variabili private per nascondere i dettagli di implementazione. La tua classe A potrebbe utilizzare un array per archiviare i punteggi. Domani potresti preferire utilizzare tree o priority queue . Tutti gli utenti della tua classe hanno bisogno di un modo per inserire punteggi e nomi addScore() e un modo per capire chi è il 10% massimo digetTop(n).

Non hanno bisogno di accedere alla struttura dati sottostante. Sembra fantastico? Bene, ci sono alcuni avvertimenti.

Non dovresti comunque memorizzare molto stato, la maggior parte non è necessaria. Lo stato di archiviazione complicherà il codice anche quando è "segregato" in una classe specifica. Pensaci, quella variabile privata probabilmente è cambiata perché un altro oggetto ha chiamato un metodo di quella classe. Hai ancora bisogno di capire quando e dove è stato chiamato quel metodo e che il metodo pubblico può essere chiamato ovunque teoricamente.

La cosa migliore che puoi fare è limitare la quantità di stato contenuta nel tuo programma e utilizzare le funzioni pure quando possibile.

    
risposta data 03.05.2017 - 20:05
fonte
-4

Devi prima capire il concetto di programmazione orientata agli oggetti. Ha astrazione, incapsulamento ecc.

Astrazione: ottieni l'idea della logica senza bisogno di conoscere i dettagli di sottolineatura dell'implementazione.

Incapsulamento - Non è possibile vedere l'implementazione di sottolineatura dell'oggetto. Solo tu puoi vedere l'interfaccia pubblica dell'oggetto.

Ora in un'implementazione specifica con un programma orientato agli oggetti come C #, Java, C ++ puoi implementare questi concetti.

private - L'implementazione che dovrebbe essere nascosta al mondo esterno. In modo che tu possa cambiarlo e l'utente della tua classe non sia interessato.

public - Questa è l'interfaccia con cui puoi usare il tuo oggetto.

    
risposta data 27.06.2013 - 15:08
fonte

Leggi altre domande sui tag