Privato vs protetto: preoccupazione per la buona visibilità della visibilità [chiuso]


145

Ho cercato e conosco la differenza teorica.

  • public : qualsiasi classe / funzione può accedere al metodo / proprietà.
  • protetto : solo questa classe e tutte le sottoclassi possono accedere al metodo / proprietà.
  • privato : solo questa classe può accedere al metodo / proprietà. Non sarà nemmeno ereditato.

Va tutto bene e bene, la domanda è: qual è la differenza pratica tra loro? Quando useresti privatee quando useresti protected? Esiste una buona pratica standard o accettabile su questa?

Fino ad ora, per mantenere il concetto di ereditarietà e polimorfismo, uso publicper tutto ciò a cui si dovrebbe accedere dall'esterno (come costruttori e funzionalità della classe principale), e protectedper metodi interni (logica, metodi di supporto, ecc.). Sono sulla buona strada?

(Nota che questa domanda è per me, ma anche per riferimento futuro in quanto non ho visto una domanda come questa SO).


33
Importa? Qualsiasi lingua con supporto OOP ha questa preoccupazione. Mi capita di programmare in PHP, ma penso che la domanda si applichi a qualsiasi linguaggio di supporto OOP.
Madara's Ghost

2
Va bene, mi chiedo se ti sei dimenticato di taggare. Ora vedo il tag oop.
Paul Bellora,

Devo impostare la visibilità (privata / pubblica / protetta) per ogni proprietà della classe? O solo alcuni di loro devono avere un tipo di visibilità? Se sì, come decidere quale proprietà deve avere una visibilità impostata in cima alla classe?
user4271704

1
a prima vista, la differenza tra protetto e privato sembra evidente. Usa protetto se le sottoclassi utilizzeranno il metodo / la variabile, altrimenti usa privato. In particolare, se le sottoclassi dovessero ridefinire una variabile privata molto simile nel genitore, è sufficiente proteggerla.
iPherian,

Esiste un altro identificatore di accesso internalin linguaggio C # che limita il livello di accesso di una classe o dei suoi membri all'interno di un assembly fisico. Tuttavia, non sono sicuro del suo supporto o di qualcosa di simile in altre lingue.
RBT

Risposte:


128

No, non sei sulla strada giusta. Una buona regola empirica è: rendere tutto il più privato possibile. Ciò rende la tua classe più incapsulata e consente di modificare gli interni della classe senza influire sul codice utilizzando la classe.

Se progetti che la tua classe sia ereditabile, allora scegli con attenzione cosa può essere sovrascritto e accessibile dalle sottoclassi e rendilo protetto (e finale, parlando di Java, se vuoi renderlo accessibile ma non scavalcabile). Tuttavia, tenere presente che, non appena si accetta di avere sottoclassi della propria classe e esiste un campo o metodo protetto, questo campo o metodo fa parte dell'API pubblica della classe e non può essere modificato in seguito senza interrompere le sottoclassi.

Una classe che non è destinata a essere ereditata dovrebbe essere resa definitiva (in Java). Potresti allentare alcune regole di accesso (da privato a protetto, da finale a non finale) per motivi di unit test, ma poi documentalo e chiarisci che, sebbene il metodo sia protetto, non dovrebbe essere ignorato.


1
La vera domanda qui è su privatevs protectedQuando voglio ereditare una proprietà, e quando no? Non so davvero se un utente a volte in futuro vuole prendere la mia classe ed estenderla ...
Madara's Ghost

16
Bene, la domanda non è ciò che l'utente vuole sovrascrivere, la domanda è cosa vuoi permettere di essere sovrascritto. Di solito aiuta a cambiare lato e provare a pensare: se usassi quella classe e creassi una sottoclasse, cosa vorrei poter scavalcare? A volte altri utenti mancheranno ancora le cose ...
Thorsten Dittmar il

3
I campi protetti sono cattive pratiche in eredità! . Per favore, state attenti. Ecco perché
RBT

4
I campi privati ​​in pratica sono cattivi in ​​eredità !. (esagerando) Per favore leggi la mia risposta.
Larry

6
Opinione tipica da maniaco del controllo.
bruno desthuilliers,

58

Consentitemi di prefigurarlo dicendo che sto parlando principalmente dell'accesso al metodo qui e, in misura leggermente minore, che segna le classi come finali, non l' accesso dei membri.

La vecchia saggezza

"contrassegnalo come privato a meno che tu non abbia una buona ragione per non farlo"

aveva senso nei giorni in cui era stato scritto, prima che open source dominasse lo spazio della libreria per sviluppatori e VCS / dependency mgmt. divenne iper collaborativo grazie a Github, Maven, ecc. All'epoca c'erano anche soldi da fare limitando il modo (i) in cui una biblioteca poteva essere utilizzata. Ho trascorso probabilmente i primi 8 o 9 anni della mia carriera aderendo rigorosamente a questa "best practice".

Oggi credo che sia un cattivo consiglio. A volte c'è un'argomentazione ragionevole per contrassegnare un metodo come privato o un finale di classe ma è estremamente raro, e anche allora probabilmente non sta migliorando nulla.

Hai mai:

  • Sei stato deluso, sorpreso o ferito da una libreria ecc. Che aveva un bug che avrebbe potuto essere risolto con ereditarietà e poche righe di codice, ma a causa di metodi e classi privati ​​/ finali sono stati costretti ad aspettare una patch ufficiale che potrebbe non arrivare mai? Io ho.
  • Volevi usare una libreria per un caso d'uso leggermente diverso da quello immaginato dagli autori ma non sei stato in grado di farlo a causa di metodi e classi privati ​​/ finali? Io ho.
  • Sei stato deluso, sorpreso o ferito da una biblioteca ecc. Che era troppo permissivo nella sua estensibilità? No.

Queste sono le tre maggiori razionalizzazioni che ho sentito per impostazione predefinita dei metodi di marcatura:

Razionalizzazione n. 1: non è sicuro e non c'è motivo di ignorare un metodo specifico

Non riesco a contare il numero di volte in cui ho sbagliato a stabilire se ci sarà o meno la necessità di sostituire un metodo specifico che ho scritto. Avendo lavorato su diverse popolari librerie open source, ho imparato a mie spese il vero costo di contrassegnare le cose come private. Spesso elimina l'unica soluzione pratica a problemi imprevisti o casi d'uso. Al contrario, non l'ho mai fatto rimpianto in oltre 16 anni di sviluppo professionale di contrassegnare un metodo protetto anziché privato per motivi legati alla sicurezza delle API. Quando uno sviluppatore sceglie di estendere una classe e sovrascrivere un metodo, stanno consapevolmente dicendo "So cosa sto facendo". e per motivi di produttività dovrebbe essere sufficiente. periodo. Se è pericoloso, annotalo nella classe / metodo Javadocs, non chiudere semplicemente alla cieca la porta.

I metodi di marcatura protetti per impostazione predefinita sono una mitigazione di uno dei principali problemi dello sviluppo del software moderno: fallimento dell'immaginazione.

Razionalizzazione n. 2: mantiene pulite le API pubbliche / Javadocs

Questo è più ragionevole e, a seconda del pubblico di destinazione, potrebbe anche essere la cosa giusta da fare, ma vale la pena considerare quale sia il costo di mantenere "pulito" l'API: estensibilità. Per i motivi sopra menzionati, probabilmente ha più senso contrassegnare le cose protette di default per ogni evenienza.

Razionalizzazione n. 3: il mio software è commerciale e devo limitarne l'uso.

Anche questo è ragionevole, ma come consumatore andrei con il concorrente meno restrittivo (supponendo che non esistano differenze significative di qualità) ogni volta.

Mai dire mai

Non sto dicendo di non contrassegnare mai i metodi come privati. Sto dicendo che la migliore regola empirica è "rendere i metodi protetti a meno che non ci sia una buona ragione per non farlo".

Questo consiglio è più adatto a coloro che lavorano su biblioteche o progetti su larga scala che sono stati suddivisi in moduli. Per progetti più piccoli o più monolitici non ha molta importanza poiché controlli comunque tutto il codice ed è facile cambiare il livello di accesso del tuo codice se / quando ne hai bisogno. Anche allora, darei comunque lo stesso consiglio :-)


Scritto tuo Rationalization # 1- Quando segnare qualcosa come protetto, scelgo di farlo esplicitamente poiché ritengo che questo comportamento non sia definitivo e potrebbe essere un caso in cui le mie classi secondarie vorrebbero ignorarlo . Se non sono così sicuro, vorrei renderlo privato per impostazione predefinita. Soprattutto in un linguaggio come C # che mantiene un metodo non virtuale per impostazione predefinita, l'aggiunta di un identificatore di accesso protetto non ha senso. Devi aggiungere sia parole chiave virtualche protectedparole chiave per rendere il tuo intento molto chiaro. Inoltre, le persone preferiscono l'associazione rispetto all'eredità, quindi protectedè difficile percepire il default
RBT

4
La combinazione di parole chiave necessarie per rendere sovrascrivibile un metodo è un IMHO dettagliato nel linguaggio. L'argomentazione contraria che presento alla razionalizzazione n. 1 si rivolge direttamente al caso in cui menzioni "Se non ne sono così sicuro ...". In pratica, se non riesci a pensare a un motivo per cui sarebbe pericoloso, allora c'è di più da guadagnare optando per l'estensibilità. Quando dici "associazione" lo considero un sinonimo di composizione, nel qual caso non vedo che sia importante in entrambi i modi.
Nick,

3
Non ricordo quante volte ho dovuto "clonare" le classi sottostanti solo perché volevo sovrascrivere 1 o 2 o i metodi. E come quello che hai detto, con la tendenza social che stiamo condividendo più codice che mai, ha molto più senso andare con protetto che privato di default. cioè essere più aperti alle proprie logiche.
shinkou,

1
Essere d'accordo. Volevo solo modificare BufferedReader di Java per gestire i delimitatori di linea personalizzati. Grazie ai campi privati ​​devo clonare l'intera classe piuttosto che semplicemente estenderla e sovrascrivere readLine ().
Ron Dunn,

21

Smetti di abusare dei campi privati ​​!!!

I commenti qui sembrano essere di grande aiuto per l'utilizzo di campi privati. Bene, allora ho qualcosa di diverso da dire.

I campi privati ​​sono buoni in linea di principio? Sì. Ma dire che una regola d'oro è rendere tutto privato quando non sei sicuro è sicuramente sbagliato ! Non vedrai il problema finché non ne incontrerai uno. Secondo me, dovresti contrassegnare i campi come protetti se non sei sicuro .

Esistono due casi in cui si desidera estendere una classe:

  • Vuoi aggiungere funzionalità extra a una classe base
  • Vuoi modificare la classe esistente che è al di fuori del pacchetto corrente (forse in alcune librerie)

Non c'è niente di sbagliato nei campi privati ​​nel primo caso. Il fatto che la gente stia abusando dei campi privati lo rende così frustrante quando scopri che non puoi modificare la merda.

Prendi in considerazione una semplice libreria che modella le auto:

class Car {
    private screw;
    public assembleCar() {
       screw.install();
    };
    private putScrewsTogether() {
       ...
    };
}

L'autore della biblioteca ha pensato: non c'è motivo per cui gli utenti della mia biblioteca debbano accedere ai dettagli di implementazione di assembleCar()right? Contrassegniamo la vite come privata.

Bene, l'autore ha torto. Se vuoi modificare solo il assembleCar()metodo senza copiare l'intera classe nel tuo pacchetto, sei sfortunato. Devi riscrivere il tuo screwcampo. Diciamo che questa macchina usa una dozzina di viti e ognuna di esse comporta un codice di inizializzazione non banale in diversi metodi privati, e queste viti sono tutte contrassegnate come private. A questo punto, inizia a succhiare.

Sì, puoi discutere con me che l'autore della biblioteca avrebbe potuto scrivere un codice migliore, quindi non c'è niente di sbagliato nei campi privati . Non sto sostenendo che il campo privato sia un problema con OOP . È un problema quando le persone li usano.

La morale della storia è che, se stai scrivendo una biblioteca, non sai mai se i tuoi utenti vogliono accedere a un determinato campo. Se non sei sicuro, contrassegnalo in protectedmodo che tutti siano più felici in seguito. Almeno non abusare del campo privato .

Sostengo moltissimo la risposta di Nick.


2
L'uso dei campi privati ​​sta principalmente dicendo che l'eredità è l'astrazione sbagliata per alterare un certo comportamento di una classe / oggetto (se usato consapevolmente). Le modifiche in genere violano silenziosamente LSP poiché il comportamento viene modificato senza modificare l'API. Ottima risposta, +1.
Madara's Ghost

Predicare! Il privato è la rovina della mia esistenza quando provo a scrivere mod di Minecraft. Niente di più fastidioso che dover usare Reflection o ASM per risolvere il problema.
RecursiveExceptionException il

Come ho capito la risposta di Nick, si riferiva principalmente a metodi e non a proprietà. Sono totalmente d'accordo sul fatto che non dovremmo dichiarare i metodi privati ​​ogni volta e l'uso di tali bisogni deve essere ponderato, ma cmon perché consiglieresti di dichiarare le proprietà protette su quelle private solo perché vuoi "riscrivere" più facilmente la classe. Condividi totalmente la tua frustrazione mentre lavoro quotidianamente con 3rd p, ma incoraggiamo le persone a creare un'architettura solida e non solo dicendo "il codice finirebbe per essere una schifezza, quindi proteggiamoci dall'inizio in modo da poterlo riscrivere facilmente". Anche se condivido la tua frustrazione.
sean662,

16

Ho letto un articolo qualche tempo fa che parlava di bloccare ogni classe il più possibile. Rendi tutto definitivo e privato a meno che tu non abbia un bisogno immediato di esporre alcuni dati o funzionalità al mondo esterno. È sempre facile espandere l'ambito per essere più ammissibili in seguito, ma non viceversa. Innanzitutto considera di fare quante più cose possibili finalche renderanno la scelta tra privatee protectedmolto più semplice.

  1. Rendi tutte le classi definitive a meno che non sia necessario sottoclassarle immediatamente.
  2. Rendi definitivi tutti i metodi a meno che non sia necessario sottoclassare e sovrascriverli immediatamente.
  3. Rendi definitivi tutti i parametri del metodo, a meno che non sia necessario modificarli all'interno del corpo del metodo, il che è comunque un po 'imbarazzante la maggior parte delle volte.

Ora, se rimani con una classe finale, allora rendi tutto privato a meno che qualcosa non sia assolutamente necessario per il mondo: rendilo pubblico.

Se ti rimane una classe che ha sottoclassi (es), allora esamina attentamente ogni proprietà e metodo. Innanzitutto considera se desideri persino esporre quella proprietà / metodo a sottoclassi. In tal caso, considera se una sottoclasse può causare danni al tuo oggetto se incasina il valore della proprietà o l'implementazione del metodo nel processo di sostituzione. Se è possibile e vuoi proteggere la proprietà / il metodo della tua classe anche dalle sottoclassi (sembra ironico, lo so), allora rendilo privato. Altrimenti rendilo protetto.

Disclaimer: non programma molto in Java :)


2
Per chiarire: A final classin Java è una classe da cui non è possibile ereditare. A final methodnon è virtuale, ovvero non sostituibile da una sottoclasse (i metodi Java sono virtuali per impostazione predefinita). A final field/parameter/variableè un riferimento variabile immutabile (nel senso che non può essere modificato dopo l'inizializzazione).
M. Stramm,

1
Stranamente, ho visto l'esatto consiglio opposto, che nulla dovrebbe essere definitivo a meno che non sia certo che ignorare la cosa in questione abbia il potenziale per spezzare un invariante. In questo modo chiunque è libero di estendere le tue lezioni secondo necessità.
GordonM,

Puoi indicare un caso nella tua carriera di programmazione in cui contrassegnare un parametro di metodo "finale" ha fatto la differenza per la tua comprensione? (diverso da quello richiesto dal compilatore)
user949300

7

Quando useresti privatee quando useresti protected?

Si può pensare all'ereditarietà privata implementata in termini di relazione piuttosto che in unarelazione IS-A . In poche parole, l'interfaccia esterna della classe ereditaria non ha alcuna relazione (visibile) con la classe ereditata, utilizza l'privateereditarietà solo per implementare una funzionalità simile fornita dalla classe Base.

A differenza dell'ereditarietà privata, l' eredità protetta è una forma ristretta di eredità, in cui la classe derivante IS-A tipo di classe Base e desidera limitare l'accesso dei membri derivati ​​solo alla classe derivata.


2
Che "Implementato in termini di relazione" è una relazione HAS-A. Se tutti gli attributi sono privati, l'unico modo per accedervi è attraverso i metodi. È la stessa cosa che se la sottoclasse contenesse un oggetto della superclasse. Eliminare tutti gli attributi protetti dalle tue classi concrete significa eliminare anche da loro tutte le eredità.
shawnhcorey,

2
Processo di pensiero interessante - Ereditarietà privata . @shawnhcorey grazie per aver reso esplicito che punta verso la relazione HAS-A aka associazione (che può essere aggregazione o composizione). Ritengo che anche questo post debba essere aggiornato per chiarire lo stesso.
RBT

1

Bene, si tratta di incapsulamento se le classi di pagamento pagano la fatturazione del pagamento, quindi nella classe di prodotto perché dovrebbe essere necessario l'intero processo di fatturazione, ovvero il metodo di pagamento, come pagare dove pagare .. quindi lasciare solo ciò che viene utilizzato per altre classi e oggetti niente di più di quel pubblico per coloro che userebbero anche altre classi, protetto per quei limiti solo per l'estensione delle classi. Dato che sei Madara Uchiha, il privato è come "limboo"puoi vederlo (classe solo una classe).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.