2013-01-18 25 views
29

Sto facendo un grande progetto per la prima volta. Ho un sacco di classi e alcuni di loro hanno variabili pubbliche, alcuni hanno variabili private con metodi setter e getter e lo stesso con entrambi i tipi.Devo utilizzare variabili pubbliche o private?

Ho deciso di riscrivere questo codice per utilizzare principalmente un solo tipo. Ma non so quale debba essere usato (le variabili utilizzate solo per i metodi nello stesso oggetto sono sempre private e non sono soggette a questa domanda).

Conosco la teoria che cosa significa pubblico e privato, ma cosa viene utilizzato nel mondo reale e perché?

+0

La risposta OO sarebbe quella di utilizzare variabili private. Ma il modello di visibilità in C++ è abbastanza rotto (un po 'meno in C++ 11) e i membri privati ​​possono causare problemi molto sorprendenti se non si danno loro nomi uguali. –

+8

@MarcGlisse Wut? – Collin

+0

Quasi una vittima precisa: http://stackoverflow.com/questions/1596432/getter-and-setter-pointers-or-references-and-good-syntax-to-use-in-c –

risposta

30

private i membri di dati sono generalmente considerati buoni perché forniscono l'incapsulamento.

Fornire getter e setter per loro interrompe l'incapsulamento, ma è comunque migliore dei membri di dati public perché c'è una sola volta il punto di accesso a quei dati.

Noterete questo durante il debug. Se è privato, è noto è possibile modificare solo la variabile all'interno della classe. Se è pubblico, dovrai cercare l'intero codice per il punto in cui è possibile che venga modificato.

Per quanto possibile, bloccare i getter/setter e creare proprietà private. Questo segue il principio di nascondere le informazioni: non dovresti preoccuparti di quali proprietà ha una classe. Dovrebbe essere autonomo. Naturalmente, in pratica questo non è fattibile, e se lo è, un progetto che segue questo sarà più disordinato e più difficile da mantenere di quello che non lo fa.

Si tratta naturalmente di una regola empirica - per esempio, mi basta usare un struct (equivalente con un class con accesso pubblico) per, diciamo, una semplice classe punto:

struct Point2D 
{ 
    double x; 
    double y; 
}; 
+0

Direi che il setter/getter crea un ulteriore punto di accesso oltre all'accesso diretto alla variabile. – sth

+0

@sth ma l'accesso diretto è limitato alla classe, quindi è qualcosa. (se questo è ciò che intendevi, allora sì) –

+3

+1 per "Il più possibile, vietare i getter/setter". Io sostengo sempre che i setter sono cattivi (le funzioni, intendo, non mi importa dei cani). – Angew

0

variabili pubbliche di solito sono scoraggiato, e la forma migliore è di fare tutte le variabili private e accedervi con getter e setter:

IDE
private int var; 

public int getVar() { 
    return var; 
} 

public void setVar(int _var) { 
    var = _var; 
} 

moderni come Eclipse e altri ti aiuti a fare questo, fornendo caratteristiche come "Implementare Getters e Setter s "e" Encapsulate Field "(che sostituisce tutti gli accessi diretti delle variabili con le chiamate getter e setter corrispondenti).

+5

Perché è meglio rendere le variabili private _e_ fornire getter e setter? Chiamare quel processo "incapsulare il campo" è fuorviante; sostituisce solo una sintassi per l'accesso con una sintassi più dettagliata per l'accesso. (Nota, la domanda è codificata in C++.) –

+4

getter e setter offrono un falso senso di incapsulamento. –

5

Non esiste una regola rigida per ciò che dovrebbe essere privato/pubblico o protetto.

Dipende dal ruolo della classe e da ciò che offre.

  • tutti i metodi ed i membri che costituiscono il funzionamento interno di la classe dovrebbe essere fatto privato.
  • Tutto ciò che una classe offre al mondo esterno dovrebbe essere pubblico.
  • Membri e metodi che potrebbero dover essere estesi in una specializzazione di questa classe, potrebbero essere dichiarati come protetto.
15

Dal momento che si dice che si conosce la teoria, e altre risposte hanno scavato nel significato di pubblico/privato, getter e setter, vorrei concentrarmi sul perché di utilizzare funzioni di accesso invece di creare attributi pubblici (dati dei membri in C++).

Immaginate di avere un camion di classe in un progetto logistico:

class Truck { 
public: 
    double capacity; 

    // lots of more things... 
}; 

a condizione che siano nordamericano, probabilmente utilizzare galloni al fine di rappresentare la capacità dei camion. Immagina che il tuo progetto sia finito, funziona perfettamente, anche se molti usi diretti di Truck::capacity sono fatti. In realtà, il tuo progetto diventa un successo, quindi alcuni studi europei ti chiedono di adattare il tuo progetto a loro; sfortunatamente, il progetto dovrebbe utilizzare ora il sistema metrico, quindi per la capacità dovrebbero essere utilizzati litri anziché litri.

Ora, questo potrebbe essere un disastro. Naturalmente, una possibilità sarebbe quella di preparare una base di codici solo per il Nord America e una base di codici solo per l'Europa. Ma questo significa che le correzioni dei bug dovrebbero essere applicate in due diverse fonti di codice, e questo è deciso per essere irrealizzabile.

La soluzione è creare una possibilità di configurazione nel progetto. L'utente dovrebbe essere in grado di impostare galloni o litri, invece di essere una scelta fissa fissa di galloni.

Con l'approccio visto sopra, questo significherà molto lavoro, dovrete rintracciare tutti gli usi di Truck::capacity e decidere cosa fare con loro. Ciò probabilmente significherà modificare i file lungo l'intera base di codice. Supponiamo, in alternativa, di aver deciso un approccio più theoretic.

class Truck { 
public: 
    double getCapacity() const 
     { return capacity; } 

    // lots of more things... 
private: 
    double capacity; 
}; 

Una possibile, il cambiamento alternativa comporta alcuna modifica all'interfaccia della classe:

class Truck { 
public: 
    double getCapacity() const 
     { if (Configuration::Measure == Gallons) { 
      return capacity; 
      } else { 
      return (capacity * 3.78); 
      } 
     } 


    // lots of more things... 
private: 
    double capacity; 
}; 

(Si prega di tenere conto int che ci sono molti modi per fare questo, quello è solo una possibilità , e questo è solo un esempio)

Dovrai creare la configurazione della classe di utilità globale (ma dovresti farlo comunque) e aggiungere un include in truck.h per configuration.h, ma queste sono tutte modifiche locali, il rimanere l'inserimento della tua base di codice rimane invariato, evitando così potenziali bug.

Infine, dichiari anche che stai lavorando ora in un grande progetto, che penso sia il tipo di campo in cui queste ragioni in realtà hanno più senso. Ricorda che l'obiettivo da tenere presente mentre lavori su grandi progetti è creare codice gestibile, cioè codice che puoi correggere ed estendere con nuove funzionalità. Puoi dimenticarti di getter e setter in piccoli progetti personali, anche se proverei a rendermi abituato a loro.

Spero che questo aiuti.

+1

So che questo è un esempio di giocattolo, e per lo più sono d'accordo con le conclusioni, ma per favore, se stai facendo girare un progetto che include più unità, ** non farlo **. Invece, memorizza tutto usando le unità SI internamente e converti solo tra unità quando interagisci con l'utente (nel livello vista se stai facendo qualcosa in remoto MVC-ish). – Nax

2

Ci sono alcuni tipi di dati il ​​cui unico scopo è di contenere dati ben specificati. Questi possono in genere essere scritti come strutture con membri di dati pubblici. A parte ciò, una classe dovrebbe definire un'astrazione. Le variabili pubbliche o banali e getter suggeriscono che il progetto non è stato pensato a sufficienza, risultando in un agglomerato di astrazioni deboli che non astraggono gran parte di nulla. Invece di pensare ai dati, pensa al comportamento : questa classe dovrebbe fare X, Y e Z. Da lì, decidere quali dati interni sono necessari per supportare il comportamento desiderato.All'inizio non è facile, ma continua a ricordarti che è un comportamento che conta, non i dati.

3

Da un punto di vista OOP getter/setter aiutare con incapsulamento e deve quindi sempre essere utilizzato. Quando chiami un getter/setter, la classe può fare tutto ciò che vuole dietro le quinte e gli interni della classe non sono esposti all'esterno.

D'altra parte, da un punto di vista del C++, può anche essere uno svantaggio se la classe fa molte cose inaspettate quando si desidera ottenere/impostare un valore. Alla gente piace sapere se alcuni accessi generano un sovraccarico enorme o sono semplici ed efficienti. Quando accedi a una variabile pubblica sai esattamente cosa ottieni, quando usi un getter/setter non hai idea.

Soprattutto se si esegue solo un piccolo progetto, passare il tempo a scrivere getter/setter e ad adattarli di conseguenza quando si decide di modificare il nome/tipo di variabile/... produce un sacco di lavoro intenso con scarso guadagno. Faresti meglio a passare quel tempo a scrivere codice che faccia qualcosa di utile.

codice C++ comunemente non usa getter/setter quando non forniscono guadagno reale. Se progetti un progetto di 1.000.000 righe con molti moduli che devono essere il più indipendenti possibile, potrebbe avere senso, ma per la maggior parte dei codici di dimensioni normali che scrivi giorno per giorno sono eccessivi.

0

variabili membro private sono preferite rispetto alle variabili membro pubbliche, principalmente per le ragioni sopra esposte (incapsulamento, dati ben specificati, ecc ..). Forniscono anche una certa protezione dei dati, poiché garantisce che nessuna entità esterna possa modificare la variabile membro senza passare attraverso il canale appropriato di un setter, se necessario.

Un altro vantaggio di getter e setter è che se si utilizza un IDE (come Eclipse o Netbeans), è possibile utilizzare la funzionalità della IDE per cercare ogni posto nella base di codice in cui viene chiamata la funzione. Forniscono visibilità su dove viene utilizzato o modificato un dato in quella particolare classe. Inoltre, puoi facilmente rendere l'accesso alle variabili membro thread safe avendo un mutex interno. Le funzioni getter/setter prenderebbero questo mutex prima di accedere o modificare la variabile.

Sono un sostenitore di astrazione al punto in cui è ancora utile. L'astrazione per motivi di astrazione di solito si traduce in un caos disordinato che è più complicato del suo valore.