Dovresti mai usare variabili membro protette? Quali sono i vantaggi e quali problemi può causare?
Dovresti mai usare variabili membro protette? Quali sono i vantaggi e quali problemi può causare?
Risposte:
Dovresti mai usare variabili membro protette?
Dipende da quanto sei esigente nel nascondere lo stato.
Se arriva uno sviluppatore e sottoclasse la tua classe, potrebbe rovinarla perché non la capisce completamente. Con i membri privati, oltre all'interfaccia pubblica, non possono vedere i dettagli specifici dell'implementazione di come vengono fatte le cose, il che ti dà la flessibilità di cambiarlo in seguito.
La sensazione generale al giorno d'oggi è che causino un accoppiamento indebito tra classi derivate e le loro basi.
Non hanno alcun vantaggio particolare rispetto a metodi / proprietà protetti (una volta potevano avere un leggero vantaggio in termini di prestazioni), e sono stati anche utilizzati maggiormente in un'epoca in cui l'ereditarietà molto profonda era di moda, cosa che al momento non è.
no particular advantage over protected methods/properties
essere no particular advantage over *private* methods/properties
?
super
costrutto per chiamare il costruttore genitore; si occuperà quindi di inizializzare le variabili di stato privato nella classe genitore.
In genere, se qualcosa non è deliberatamente concepito come pubblico, lo rendo privato.
Se si verifica una situazione in cui è necessario accedere a quella variabile o metodo privato da una classe derivata, lo cambio da privato a protetto.
Questo non accade quasi mai: non sono affatto un fan dell'ereditarietà, poiché non è un modo particolarmente buono per modellare la maggior parte delle situazioni. In ogni caso, vai avanti, niente paura.
Direi che va bene (e probabilmente il modo migliore per farlo) per la maggior parte degli sviluppatori.
Il semplice fatto è che , se qualche altro sviluppatore arriva un anno dopo e decide di aver bisogno di accedere alla tua variabile membro privato, modificherà semplicemente il codice, lo cambierà in protetto e continuerà con la propria attività.
Le uniche vere eccezioni a questo sono se sei nel business della spedizione di DLL binarie in forma di scatola nera a terze parti. Questo è costituito fondamentalmente da Microsoft, quei fornitori di "Custom DataGrid Control" e forse alcune altre grandi app fornite con librerie di estensibilità. A meno che tu non sia in quella categoria, non vale la pena dedicare tempo / fatica a preoccuparsi di questo genere di cose.
Il problema chiave per me è che una volta che si crea una variabile protetta, non è possibile consentire a nessun metodo della classe di fare affidamento sul suo valore all'interno di un intervallo, perché una sottoclasse può sempre posizionarlo fuori intervallo.
Ad esempio, se ho una classe che definisce la larghezza e l'altezza di un oggetto renderizzabile e rendo protette quelle variabili, non posso fare supposizioni (ad esempio) sulle proporzioni.
Criticamente, non posso mai fare queste supposizioni in nessun momento dal momento in cui il codice viene rilasciato come libreria, poiché anche se aggiorno i miei setter per mantenere le proporzioni, non ho alcuna garanzia che le variabili vengano impostate tramite i setter o accessibili tramite il getter nel codice esistente.
Né nessuna sottoclasse della mia classe può scegliere di fare quella garanzia, in quanto non può nemmeno applicare i valori delle variabili, anche se questo è l'intero punto della loro sottoclasse .
Come esempio:
Vincolando le variabili a essere private, posso quindi applicare il comportamento che desidero tramite setter o getter.
In generale, terrei le tue variabili membro protette nel raro caso in cui hai il controllo totale anche sul codice che le utilizza. Se stai creando un'API pubblica, direi mai. Di seguito, faremo riferimento alla variabile membro come una "proprietà" dell'oggetto.
Ecco cosa non può fare la tua superclasse dopo aver protetto una variabile membro anziché privata con funzioni di accesso:
creare pigramente un valore al volo quando la proprietà viene letta. Se aggiungi un metodo getter protetto, puoi creare pigramente il valore e restituirlo.
sapere quando la proprietà è stata modificata o eliminata. Questo può introdurre bug quando la superclasse fa ipotesi sullo stato di quella variabile. La creazione di un metodo setter protetto per la variabile mantiene quel controllo.
Impostare un punto di interruzione o aggiungere un output di debug quando la variabile viene letta o scritta.
Rinomina quella variabile membro senza cercare in tutto il codice che potrebbe utilizzarla.
In generale, penso che sarebbe il raro caso in cui consiglierei di creare una variabile membro protetta. È meglio passare qualche minuto a esporre la proprietà tramite getter / setter che ore dopo rintracciare un bug in qualche altro codice che ha modificato la variabile protetta. Non solo, ma sei assicurato contro l'aggiunta di funzionalità future (come il caricamento lento) senza interrompere il codice dipendente. È più difficile farlo più tardi che farlo ora.
A livello di progettazione potrebbe essere appropriato utilizzare una proprietà protetta, ma per l'implementazione non vedo alcun vantaggio nel mappare questo a una variabile membro protetta piuttosto che ai metodi accessor / mutator.
Le variabili membro protette presentano svantaggi significativi perché consentono effettivamente al codice client (la sottoclasse) di accedere allo stato interno della classe di base. Ciò impedisce alla classe base di mantenere efficacemente le sue invarianti.
Per lo stesso motivo, le variabili membro protette rendono anche la scrittura di codice multi-thread sicuro molto più difficile a meno che non sia garantita costante o limitata a un singolo thread.
I metodi accessorio / mutatore offrono una stabilità dell'API notevolmente maggiore e flessibilità di implementazione durante la manutenzione.
Inoltre, se sei un purista OO, gli oggetti collaborano / comunicano inviando messaggi, non leggendo / impostando lo stato.
In cambio offrono pochissimi vantaggi. Non li rimuoverei necessariamente dal codice di qualcun altro, ma non li uso io stesso.
Il più delle volte, è pericoloso usare protected perché rompi in qualche modo l'incapsulamento della tua classe, che potrebbe essere scomposta da una classe derivata mal progettata.
Ma ho un buon esempio: diciamo che puoi usare una sorta di contenitore generico. Ha un'implementazione interna e funzioni di accesso interne. Ma devi offrire almeno 3 accessi pubblici ai suoi dati: map, hash_map, vector-like. Quindi hai qualcosa come:
template <typename T, typename TContainer>
class Base
{
// etc.
protected
TContainer container ;
}
template <typename Key, typename T>
class DerivedMap : public Base<T, std::map<Key, T> > { /* etc. */ }
template <typename Key, typename T>
class DerivedHashMap : public Base<T, std::hash_map<Key, T> > { /* etc. */ }
template <typename T>
class DerivedVector : public Base<T, std::vector<T> > { /* etc. */ }
Ho usato questo tipo di codice meno di un mese fa (quindi il codice è dalla memoria). Dopo un po 'di riflessione, credo che mentre il contenitore Base generico dovrebbe essere una classe astratta, anche se può vivere abbastanza bene, perché usare direttamente Base sarebbe un tale problema dovrebbe essere vietato.
Riepilogo In questo modo sono stati protetti i dati utilizzati dalla classe derivata. Tuttavia, dobbiamo tenere conto del fatto che la classe Base dovrebbe essere astratta.
protected
non è più incapsulato di public
. Sono disposto a essere smentito. Tutto quello che devi fare è scrivere una classe con un membro protetto e proibirmi di modificarlo. Ovviamente la classe deve essere non finale, poiché l'intero scopo dell'utilizzo di protected è per l'ereditarietà. O qualcosa è incapsulato o non lo è. Non esiste uno stato intermedio.
In breve, sì.
Le variabili membro protette consentono l'accesso alla variabile da qualsiasi sottoclasse e da qualsiasi classe nello stesso pacchetto. Questo può essere molto utile, soprattutto per i dati di sola lettura. Non credo che siano mai necessari, tuttavia, perché qualsiasi utilizzo di una variabile membro protetta può essere replicato utilizzando una variabile membro privata e un paio di getter e setter.
Per informazioni dettagliate sui modificatori di accesso .Net vai qui
Non ci sono vantaggi o svantaggi reali per le variabili membro protette, è una questione di ciò di cui hai bisogno nella tua situazione specifica. In generale è pratica accettata dichiarare le variabili membro come private e abilitare l'accesso esterno tramite le proprietà. Inoltre, alcuni strumenti (ad esempio alcuni mappatori O / R) si aspettano che i dati degli oggetti siano rappresentati da proprietà e non riconoscono le variabili membro pubbliche o protette. Ma se sai che vuoi che le tue sottoclassi (e SOLO le tue sottoclassi) accedano a una certa variabile, non c'è motivo per non dichiararla protetta.