Perché hanno campi privati, non è abbastanza protetto?


265

La visibilità privatedei campi / proprietà / attributi della classe è utile? In OOP, prima o poi, farai una sottoclasse di una classe e in quel caso, è bene capire ed essere in grado di modificare completamente l'implementazione.

Una delle prime cose che faccio quando faccio una sottoclasse di una classe è cambiare un sacco di privatemetodi protected. Tuttavia, nascondere i dettagli dal mondo esterno è importante, quindi abbiamo bisogno protectede non solo public.

La mia domanda è: conosci un caso d'uso importante in cui, privateinvece di protectedessere un buon strumento, o due opzioni " protected& public" sarebbero sufficienti per le lingue OOP?



236
Per i downvoter: anche se non sono affatto d'accordo con le premesse del PO, sto votando questa domanda perché è perfettamente coerente e vale la pena rispondere. Sì, è necessario dire al PO perché questo è sbagliato, ma il modo per farlo è quello di scrivere una risposta (o suggerire modifiche alle risposte esistenti), non di sottovalutare solo perché non l'ha ancora capito da solo.
Ixrec,

18
Le classi derivate fanno parte del mondo esterno.
Codici A Caos l'

20
Non dimenticare che protetto non significa sempre che l'accesso sia bloccato nella gerarchia dell'ereditarietà. In Java, garantisce anche l'accesso a livello di pacchetto.
berry120,

8
Il mio professore diceva che "Ci sono cose che non direi ai miei figli. Quelli che ho nei miei campi privati".
SáT

Risposte:


225

Perché come dici tu, protectedti lascia ancora con la possibilità di "modificare completamente l'implementazione". Non protegge veramente nulla all'interno della classe.

Perché ci preoccupiamo di "proteggere veramente" le cose all'interno della classe? Perché altrimenti sarebbe impossibile modificare i dettagli dell'implementazione senza rompere il codice client . Detto in altro modo, le persone che scrivono sottoclassi sono anche "il mondo esterno" per la persona che ha scritto la classe base originale.

In pratica, i protectedmembri sono essenzialmente una "API pubblica per sottoclassi" di classe e devono rimanere stabili e retrocompatibili tanto quanto i publicmembri. Se non avessimo la possibilità di creare privatemembri veri , nulla in un'implementazione sarebbe mai sicuro da cambiare, perché non si sarebbe in grado di escludere la possibilità che il codice client (non dannoso) sia riuscito in qualche modo a dipendere da esso.

Per inciso, mentre "In OOP, prima o poi, farai una sottoclasse di una classe" è tecnicamente vero, il tuo argomento sembra dare l'assunto molto più forte che "prima o poi, farai una sottoclasse di ogni classe "che non è quasi certamente il caso.


11
Se potessi, ti lancerei più di +1, poiché questa è la prima volta in privateassoluto che ha senso per me. La prospettiva della lib deve essere usata più spesso, altrimenti chiunque sia appassionato della mentalità del codice "controllo assoluto, responsabilità assoluta" (tipo C) potrebbe contrassegnarla come "proteggermi da me stesso". Nota che ho ancora usato in privateprecedenza, ho sempre sentito una buona documentazione e una convenzione di denominazione come _fooquella che indica che probabilmente non dovresti sbagliare era equivalente se non migliore. Essere in grado di dire in modo deterministico che "nulla si romperà" è una caratteristica legittima e privateunica.
abluejelly,

Inizialmente ho ignorato il caso del codice delle librerie e dei framework pubblici e ho pensato più o meno solo in termini di "codice client". L'ottimizzazione dell'implementazione interna è un buon esempio per la mia domanda, anche se mi chiedo se questo accade davvero nella realtà (specialmente quando molte persone raccomandano che una classe non dovrebbe essere più lunga di 1000 righe di codice). In generale, mi piace l'approccio di Ruby, in cui il privato è una specie di raccomandazione: "Ecco i draghi, procedi con cura".
Adam Libuša,

9
@ AdamLibuša Anche se questo è un affare molto più grande se il tuo codice è pubblico, si applica anche se sei l'autore di tutti i clienti della classe. Il problema cambia semplicemente dal fatto che alcuni refattori sono impossibili per alcuni refattori che sono noiosi e soggetti a errori. L'ottimizzazione è in realtà la ragione meno comune per questi refattori nella mia esperienza (anche se faccio principalmente Javascript), di solito è più simile a un bug che ha esposto un difetto di implementazione fondamentale che richiede la ristrutturazione del grafico di dipendenza / chiamata dei vari bit interni per raggiungere una soluzione davvero solida.
Ixrec,

5
"" prima o poi, farai una sottoclasse di ogni classe "non è quasi certamente il caso." E più precisamente, quasi sicuramente no, e NON DOVREBBE certamente, ignorare ogni funzione in una classe e modificare l'utilizzo di ogni elemento di dati in una classe. Alcune cose dovrebbero essere logicamente scritte in pietra affinché la classe abbia un significato.
Jay,

1
Ti lancio più di +1 se potessi => Questo è ciò che chiamiamo bounty @abluejelly
Thomas Ayoub

256

In OOP, prima o poi, farai una sottoclasse di una classe

Questo è sbagliato. Non tutte le classi sono destinate a essere sottoclassate e alcuni linguaggi OOP tipicamente statici hanno anche funzionalità per impedirlo, ad esempio final(Java e C ++) o sealed(C #).

è bene capire ed essere in grado di modificare completamente l'implementazione.

No non lo è. È positivo che una classe sia in grado di definire chiaramente la sua interfaccia pubblica e preservare i suoi invarianti anche se è ereditata.

In generale, il controllo degli accessi riguarda la compartimentazione. Volete che una singola parte del codice sia compresa senza dover comprendere in dettaglio come interagisce con il resto del codice. L'accesso privato lo consente. Se tutto è almeno protetto, devi capire cosa fa ogni sottoclasse per capire come funziona la classe base.

O per dirla nei termini di Scott Meyers: le parti private di una classe sono influenzate da una quantità finita di codice: il codice della classe stessa.

Le parti pubbliche sono potenzialmente interessate da ogni bit di codice esistente e da ogni bit di codice ancora da scrivere, che è una quantità infinita di codice.

Le parti protette sono potenzialmente interessate da ogni sottoclasse esistente e da ogni sottoclasse ancora da scrivere, che è anche una quantità infinita di codice.

La conclusione è che la protezione ti dà molto di più rispetto al pubblico, mentre il privato ti dà un reale miglioramento. È l'esistenza dell'identificatore di accesso protetto che è discutibile, non privato.


34
+1 per lo scenario teorico di "influenzato da una quantità infinita di codice"
Broken_Window

13
Forse vale la pena notare anche che i progettisti di C # hanno effettivamente deciso di vietare la sostituzione di un metodo ereditato a meno che non sia esplicitamente contrassegnato come virtual, per i motivi indicati in questa risposta.
Will Vousden,

11
O semplicemente: protectedè per i metodi, non per i membri dei dati.
Matthieu M.

7
Non riesco a trovarlo ora, ma ricordo di aver letto una risposta ben scritta di @EricLippert sul perché sealedgrandi parti della biblioteca di .net e IIRC perché gli sarebbe piaciuto aver bloccato il resto. Nel momento in cui consenti agli eredi di terze parti di iniziare a toccare i valori interni, devi aggiungere un'enorme quantità di controlli di convalida / integrità a ogni metodo perché non puoi più fidarti di alcun invariante di progettazione sullo stato interno degli oggetti.
Dan Neely,

4
@ ThorbjørnRavnAndersen "durante lo sviluppo ma non quando rilasciato" - come mai una classe dovrebbe saperlo? Vuoi davvero compilare una versione di prova diversa dalla versione rilasciata quando non riesci nemmeno a testare la versione di rilascio? I test non dovrebbero comunque avere accesso ad elementi privati.
Sebastian Redl,

33

Sì, i campi privati ​​sono assolutamente necessari. Proprio questa settimana avevo bisogno di scrivere un'implementazione del dizionario personalizzata in cui controllavo ciò che veniva inserito nel dizionario. Se il campo del dizionario fosse stato reso pubblico o protetto, i controlli che avevo scritto con tanta cura avrebbero potuto essere facilmente elusi.

I campi privati ​​riguardano in genere la garanzia che i dati siano come previsto dal programmatore originale. Rendi tutto protetto / pubblico e guidi un pullman e cavalli attraverso tali procedure e convalide.


2
+1 per "cavalcare un pullman e cavalli attraverso" qualsiasi cosa. È un'ottima frase che vorrei aver sentito di più.
Nic Hartley,

2
Se qualcuno ha bisogno di sottoclassare la tua classe, forse dovrebbe avere accesso alle tue garanzie? E se non vuoi che qualcuno cambi le tue garanzie per raggiungere qualche obiettivo, forse non condividi il codice? Funziona così in Ruby: il privato è più o meno una raccomandazione.
Adam Libuša,

3
@ AdamLibuša "Non condividere il codice"? Non appena pubblichi alcuna DLL, condividi il codice: tutte le strutture, i metodi e tutto ciò che è visibile a tutto il mondo, in particolare con i linguaggi che supportano la riflessione per impostazione predefinita. Tutti possono fare quello che vogliono con "il tuo codice" - privateè solo un modo per dire "non toccarli" e farli applicare nel contratto del compilatore. In sistemi come .NET, questo ha anche importanti implicazioni per la sicurezza: puoi toccare i privati ​​di altri solo quando hai piena fiducia (sostanzialmente l'equivalente dell'accesso admin / root).
Luaan,

2
@ AdamLibuša Penso che la tua confusione derivi principalmente dai diversi approcci OOP adottati da lingue diverse. La radice di OOP (come originariamente definito) è la messaggistica - ciò significa che tutto è privato, tranne i messaggi a cui rispondi. Nella maggior parte delle lingue OOPish, questo è esposto come "mantieni i tuoi dati privati" - un modo per rendere l'interfaccia pubblica il più piccola possibile. L'unico modo in cui l'utente (che si tratti di una sottoclasse o di un'altra classe) deve manipolare la tua classe è attraverso l'interfaccia pubblica che hai definito - in modo simile a come usi abitualmente il volante e i pedali per guidare la tua auto :)
Luaan

3
@Luaan +1 per quando va bene toccare i privati ​​degli altri!
Nigel Touch

12

Quando si tenta di ragionare formalmente sulla correttezza di un programma orientato agli oggetti , è tipico utilizzare un approccio modulare che coinvolge gli invarianti di oggetti . In questo approccio

  1. I metodi hanno associato con loro condizioni pre e post (contratti).
  2. Gli oggetti sono associati a loro invarianti.

Il ragionamento modulare su un oggetto procede come segue (almeno per prima approssimazione)

  1. Dimostra che il costruttore dell'oggetto stabilisce l'invariante
  2. Per ogni metodo non privato, supponiamo che l'oggetto invariante e il metodo precondizionato mantengano l'entrata, quindi dimostrano che il corpo del codice implica che il postcondizione e l'invariante mantengano l'uscita del metodo

Immagina di verificare di object Autilizzare l'approccio sopra. E ora desidero verificare method gdi object Bquali chiamate method fdi object A. Il ragionamento modulare ci consente di ragionare method gsenza dover riconsiderare l'implementazione di method f. A condizione che possiamo stabilire l'invariante object Ae il presupposto del method fsito di chiamata in method g,possiamo prendere la condizione di posta method fcome un riassunto del comportamento della chiamata di metodo. Inoltre sapremo anche che dopo la chiamata restituisce l'invariante delle Asospensioni.

Questa modularità del ragionamento è ciò che ci consente di pensare formalmente a programmi di grandi dimensioni. Possiamo ragionare su ciascuno dei metodi individualmente e quindi comporre i risultati di questo ragionamento a sua volta a ragionare su parti più grandi del programma.

I campi privati sono molto utili in questo processo. Per sapere che l'invariante di un oggetto continua a rimanere tra due chiamate di metodo su quell'oggetto, in genere facciamo affidamento sul fatto che l'oggetto non viene modificato nel periodo intermedio.

Perché il ragionamento modulare funzioni in un contesto in cui gli oggetti non hanno campi privati, allora dovremmo avere un modo per garantire che qualunque campo sia stato impostato da un altro oggetto, che l'invariante sia sempre ristabilito (dopo il campo impostato). È difficile immaginare un oggetto invariante che sia valido indipendentemente dal valore dei campi dell'oggetto, ed è anche utile nel ragionamento sulla correttezza del programma. Probabilmente dovremmo inventare alcune convenzioni complicate sull'accesso ai campi. E probabilmente perderà anche una parte (nella peggiore delle ipotesi) la nostra capacità di ragionare in modo modulare.

Campi protetti

I campi protetti ripristinano parte della nostra capacità di ragionare in modo modulare. A seconda della lingua, è protectedpossibile limitare la possibilità di impostare un campo per tutte le sottoclassi o tutte le sottoclassi e le classi dello stesso pacchetto. Accade spesso che non abbiamo accesso a tutte le sottoclassi quando ragioniamo sulla correttezza di un oggetto che stiamo scrivendo. Ad esempio, potresti scrivere un componente o una libreria che verrà successivamente utilizzata in un programma più grande (o in più programmi più grandi), alcuni dei quali potrebbero non essere stati ancora scritti. In genere non saprai se e in che modo può essere suddiviso in sottoclassi.

Tuttavia, di solito spetta a una sottoclasse mantenere l'oggetto invariante della classe che estende. Quindi, in una lingua in cui la protezione significa solo "sottoclasse" e in cui siamo disciplinati per garantire che le sottoclassi mantengano sempre gli invarianti della loro superclasse, si potrebbe sostenere che la scelta di utilizzare protetto anziché privato perde solo una minima modularità .

Anche se ho parlato di ragionamenti formali, spesso si pensa che quando i programmatori ragionano in modo informale sulla correttezza del loro codice, a volte si basano anche su argomenti simili.


8

privatele variabili in una classe sono migliori rispetto protectedallo stesso motivo per cui breakun'istruzione all'interno di un switchblocco è migliore di goto labelun'istruzione; che è che i programmatori umani sono soggetti a errori.

protectedle variabili si prestano ad abusi non intenzionali (errori del programmatore), proprio come l' gotoaffermazione si presta alla creazione di spaghetti code.

È possibile scrivere un codice funzionante privo di bug usando protectedle variabili di classe? Sì, naturalmente! Proprio come è possibile scrivere codice funzionante senza bug usando goto; ma come dice il cliché "Solo perché puoi, non significa che dovresti!"

Le classi, e in effetti il ​​paradigma OO, esistono per difendersi da sfortunati programmatori umani inclini all'errore che commettono errori. La difesa contro gli errori umani è valida solo come le misure difensive integrate nella classe. Realizzare l'implementazione della tua classe protectedequivale a fare un enorme buco nelle mura di una fortezza.

Le classi base non hanno assolutamente alcuna conoscenza delle classi derivate. Per quanto riguarda una classe di base, in protectedrealtà non offre alcuna protezione in più public, perché non c'è nulla che impedisce a una classe derivata di creare un publicgetter / setter che si comporta come una backdoor.

Se una classe base consente l'accesso senza ostacoli ai dettagli di implementazione interni, allora diventa impossibile per la classe stessa difendersi dagli errori. Le classi di base non hanno assolutamente alcuna conoscenza delle loro classi derivate e quindi non hanno modo di difendersi dagli errori commessi in tali classi derivate.

La cosa migliore che una classe di base può fare è nascondere il più possibile la sua implementazione privatee mettere in atto sufficienti restrizioni per evitare di interrompere modifiche da classi derivate o qualsiasi altra cosa al di fuori della classe.

In definitiva, esistono linguaggi di alto livello per ridurre al minimo gli errori umani. Esistono anche buone pratiche di programmazione (come i principi SOLID ) per ridurre al minimo gli errori umani.

Gli sviluppatori di software che ignorano le buone pratiche di programmazione hanno una probabilità molto maggiore di fallimento e hanno maggiori probabilità di produrre soluzioni non realizzabili rotte. Coloro che seguono le buone pratiche hanno una probabilità molto più bassa di fallimento e hanno maggiori probabilità di produrre soluzioni sostenibili funzionanti.


Al downvoter: cosa potrebbe essere cambiato / migliorato in questa risposta?
Ben Cottrell,

Non ho votato in giù, ma confrontando il livello di accesso con break / goto?
imel96

@ imel96 No, confrontando il motivo per cui vengono evitati e scoraggiati dalle "Best practice" (in particolare per la scrittura di nuovo codice). vale a dire un programmatore competente eviterebbe i publicdettagli di implementazione perché si presta a un codice non mantenibile. Un programmatore competente eviterebbe gotoperché si presta a codice non mantenibile. Tuttavia, il mondo reale è tale che a volte ti perdi in un terribile pasticcio di codice legacy e non hai altra scelta che usare gotoe, per quello stesso motivo, a volte non hai altra scelta che usare dettagli di implementazione pubblici / protetti.
Ben Cottrell,

La gravità è diversa in grandezza, è incomparabile. Potrei vivere con codice che ha solo publicproprietà / interfaccia, ma non con codice che usa solo goto.
imel96

2
@ imel96 Hai mai lavorato su un codice che usa gotoo è solo perché hai letto articoli del genere? Capisco perché goto è malvagio perché ho trascorso 2 anni a lavorare nel codice antico che lo utilizza; e ho impiegato ancora più tempo a lavorare nel codice in cui "le classi" hanno i loro dettagli di implementazione trapelati ovunque publice gli identificatori friendutilizzati come hack veloci / facili / sporchi. Non accetto l'argomentazione secondo cui "esporre pubblicamente i dettagli dell'implementazione" non può causare un pasticcio di spaghetti intrecciati allo stesso livello di severità di gotoquanto sia assolutamente possibile.
Ben Cottrell,

4

Le classi ereditabili hanno due contratti: uno con i possessori di riferimenti a oggetti e con le classi derivate. I membri pubblici sono vincolati dal contratto con i titolari di riferimento e i membri protetti sono vincolati dal contratto con classi derivate.

La creazione di membri la protectedrende una classe base più versatile, ma spesso limiterà i modi in cui potrebbero cambiare le versioni future della classe. La creazione di membri privateconsente all'autore della classe una maggiore versatilità per modificare il funzionamento interno della classe, ma limita i tipi di classi che possono essere utilmente derivati ​​da essa.

Ad esempio, List<T>in .NET rende l'archivio di supporto privato; se fosse protetto, i tipi derivati ​​potrebbero fare alcune cose utili che altrimenti non sarebbero possibili, ma le versioni future di List<T>dovrebbero sempre usare il suo grosso negozio monolitico di supporto anche per elenchi che contengono milioni di articoli. Rendere privato il backing store consentirebbe alle versioni future di List<T>utilizzare un backing store più efficiente senza interrompere le classi derivate.


4

Penso che ci sia un presupposto chiave nel tuo argomento secondo cui quando qualcuno scrive una classe non sa chi potrebbe estendere quella classe lungo la strada e per quale motivo . Alla luce di questa ipotesi, la tua argomentazione avrebbe perfettamente senso perché ogni variabile che rendi privata potrebbe potenzialmente tagliare qualche strada di sviluppo lungo la strada. Tuttavia, respingerei questa ipotesi.

Se tale presupposto viene respinto, ci sono solo due casi da considerare.

  1. L'autore della classe originale aveva idee molto chiare sul perché potesse essere esteso (ad esempio, è un BaseFoo e ci saranno diverse implementazioni Foo concrete lungo la strada).

In questo caso, l'autore sa che qualcuno estenderà la classe e perché e quindi saprà esattamente cosa rendere protetto e cosa rendere privato. Stanno usando la distinzione privata / protetta per comunicare una sorta di interfaccia all'utente creando la sottoclasse.

  1. L'autore della classe figlio sta cercando di hackerare un comportamento in una classe genitore.

Questo caso dovrebbe essere raro (si potrebbe sostenere che non sia legittimo) e non si preferisce semplicemente modificare la classe originale nella base di codice originale. Potrebbe anche essere un sintomo di cattiva progettazione. In quei casi preferirei che la persona hacking nel comportamento usasse solo altri hack come gli amici (C / C ++) e setAccessible(true)(Java).

Penso che sia sicuro rifiutare questa ipotesi.

Questo generalmente ricade sull'idea della composizione piuttosto che dell'eredità . L'ereditarietà viene spesso insegnata come un modo ideale per ridurre il riutilizzo del codice, tuttavia raramente dovrebbe essere la prima scelta per il riutilizzo del codice. Non ho un semplice argomento atroce e può essere abbastanza difficile e controverso da capire. Tuttavia, nella mia esperienza con la modellazione di domini ho scoperto che uso raramente l'ereditarietà senza avere una chiara comprensione di chi erediterà la mia classe e perché.


2

Tutti e tre i livelli di accesso hanno il loro caso d'uso, OOP sarebbe incompleto senza uno di essi. Di solito lo fai

  • rendere private tutte le variabili / i dati . Non vuoi che qualcuno dall'esterno si rovini con i tuoi dati interni. Anche metodi che forniscono funzionalità ausiliarie (pensa a calcoli basati su più variabili membro) alla tua interfaccia pubblica o protetta - questo è solo per uso interno e potresti volerlo cambiare / migliorare in futuro.
  • rendere l'interfaccia generale della classe pubblica . Questo è ciò con cui dovrebbero lavorare gli utenti della tua classe originale e come pensi che dovrebbero apparire anche le classi derivate. Al fine di fornire l'incapsulamento corretto questi sono di solito solo metodi (e classi / strutture di supporto, enum, typedef, qualunque cosa l'utente abbia bisogno di lavorare con i tuoi metodi), non variabili.
  • dichiarare i metodi protetti che potrebbero essere utili per qualcuno che desidera estendere / specializzare la funzionalità della propria classe, ma non dovrebbe far parte dell'interfaccia pubblica - in effetti di solito si sollevano membri privati da proteggere quando necessario. In caso di dubbio, non lo sai, fino a quando non lo sai
    1. la tua classe può / può / sarà sottoclassata,
    2. e avere un'idea chiara di quali possano essere i casi d'uso della sottoclasse.

E ti discosti da questo schema generale solo se c'è una buona ragione ™ . Fai attenzione a "questo renderà la mia vita più semplice quando posso accedervi liberamente dall'esterno" (e qui fuori include anche le sottoclassi). Quando implemento le gerarchie di classi, spesso inizio con classi che non hanno membri protetti, fino a quando non arrivo a sottoclassare / estendere / specializzarle, diventando le classi di base di un framework / toolkit e talvolta spostando parte della loro funzionalità originale di un livello superiore.


1

Una domanda più interessante, forse, è perché è necessario qualsiasi altro tipo di campo oltre al privato. Quando una sottoclasse deve interagire con i dati di una superclasse, questa operazione crea direttamente un accoppiamento diretto tra i due, mentre l'utilizzo di metodi per fornire l'interazione tra i due consente un livello di riferimento indiretto che può consentire di apportare modifiche al superclasse che altrimenti sarebbe molto difficile.

Un certo numero di lingue (ad esempio Ruby e Smalltalk) non forniscono campi pubblici in modo che gli sviluppatori siano scoraggiati dal consentire l'accoppiamento diretto con le loro implementazioni di classe, ma perché non andare oltre e hanno solo campi privati? Non ci sarebbe alcuna perdita di generalità (perché la superclasse può sempre fornire accessi protetti per la sottoclasse), ma garantirebbe che le classi abbiano sempre almeno un piccolo grado di isolamento dalle loro sottoclassi. Perché questo non è un design più comune?


1

Molte buone risposte qui, ma comunque aggiungerò i miei due centesimi. :-)

Privato è buono per lo stesso motivo per cui i dati globali sono cattivi.

Se una classe dichiara i dati privati, allora sai assolutamente che l'unico codice che crea messaggi con questi dati è il codice nella classe. Quando c'è un bug, non devi cercare in tutta la creazione per trovare ogni posto che potrebbe cambiare questi dati. Sai che è in classe. Quando si modifica il codice e si modifica qualcosa su come viene utilizzato questo campo, non è necessario rintracciare tutti i potenziali luoghi che potrebbero utilizzare questo campo e studiare se la modifica pianificata li interromperà. Sai che gli unici posti sono all'interno della classe.

Ho avuto molte, molte volte in cui ho dovuto apportare modifiche alle classi che si trovano in una libreria e utilizzate da più app, e devo camminare molto attentamente per assicurarmi di non rompere alcune app di cui non so nulla. Maggiore è il numero di dati pubblici e protetti, maggiore è il potenziale di difficoltà.


1

Penso che valga la pena menzionare alcune opinioni dissenzienti.

In teoria, è bene avere un livello di accesso controllato per tutte le ragioni menzionate in altre risposte.

In pratica, troppo spesso durante la revisione del codice, vedo persone (a cui piace usare il privato), cambiando il livello di accesso da privato -> protetto e non troppo spesso da protetto -> pubblico. Quasi sempre, la modifica delle proprietà della classe comporta la modifica di setter / getter. Questi hanno perso gran parte del mio tempo (revisione del codice) e il loro (modifica del codice).

Mi dà anche fastidio il fatto che ciò significhi che le loro classi non sono Chiuse per modifica.

Quello era con il codice interno dove puoi sempre cambiarlo se ne hai bisogno. La situazione è peggiore con il codice di terze parti quando non è così facile cambiare il codice.

Quindi quanti programmatori pensano che sia una seccatura? Bene, quanti usano linguaggi di programmazione che non hanno un privato? Certo, le persone non usano solo quelle lingue perché non hanno specificatori privati, ma aiutano a semplificare le lingue e la semplicità è importante.

Imo è molto simile alla digitazione dinamica / statica. In teoria, la tipizzazione statica è molto buona. In pratica, impedisce solo come 2% degli errori L'irragionevole efficacia della dinamica Typing ... . L'uso di private probabilmente impedisce un errore inferiore a quello.

Penso che i principi SOLID siano buoni, vorrei che le persone si prendessero cura di loro più di quanto a loro interessasse creare un corso con pubblico, protetto e privato.


1
Se è necessario modificare il codice di terze parti quando lo si utilizza, o non è ben progettato, o non lo si sta riutilizzando bene. Puoi sempre riutilizzare una classe non astratta incapsulandola, ma in pratica non hai quasi mai bisogno di sottoclassarla.
Little Santi,

0

Vorrei anche aggiungere un altro esempio pratico del perché protectednon è abbastanza. Nella mia università i primi anni intraprendono un progetto in cui devono sviluppare una versione desktop di un gioco da tavolo (che in seguito viene sviluppata un'intelligenza artificiale ed è collegata ad altri giocatori su una rete). Viene fornito loro un codice parziale per iniziare, incluso un framework di test. Alcune delle proprietà della classe di gioco principale sono esposte in protectedmodo tale che le classi di test che estendono questa classe abbiano accesso ad esse. Ma questi campi non sono informazioni sensibili.

Come TA per l'unità, vedo spesso gli studenti semplicemente creare tutto il loro codice aggiunto protectedo public(forse perché hanno visto l'altro protectede publiccose e hanno pensato che avrebbero dovuto seguirne l'esempio). Chiedo loro perché il loro livello di protezione è inappropriato e molti non sanno perché. La risposta è che le informazioni sensibili che stanno esponendo alle sottoclassi significano che un altro giocatore può imbrogliare semplicemente estendendo quella classe e accedendo alle informazioni altamente sensibili per il gioco (essenzialmente la posizione nascosta degli avversari, immagino simile a come sarebbe se tu potevi vedere i pezzi dei tuoi avversari su una tavola da battaglia estendendo una certa classe). Ciò rende il loro codice molto pericoloso nel contesto del gioco.

Oltre a ciò, ci sono molte altre ragioni per mantenere qualcosa di privato anche nelle tue sottoclassi. Potrebbe essere nascondere dettagli di implementazione che potrebbero rovinare il corretto funzionamento della classe se cambiati da qualcuno che non sa necessariamente cosa stanno facendo (principalmente pensando ad altre persone che usano il tuo codice qui).


1
Non capisco. A meno che i giocatori non stiano estendendo dinamicamente le classi mentre il gioco è in esecuzione, come può essere usato per imbrogliare? Ciò implicherebbe che stai importando codice non attendibile nella tua base di codice. Se stai dicendo che stai dando agli studenti corsi compilati e impedendo loro di imbrogliare nel loro compito modificando i dettagli dell'implementazione, l'impostazione di un livello di protezione non renderà il compito più sicuro. Gli studenti possono decompilare le biblioteche o utilizzare la riflessione a proprio vantaggio. Questo ovviamente dipende dal livello di applicazione che stai cercando.
Sam

@sam Generalmente il codice dovrebbe essere scritto partendo dal presupposto che chiunque lo modifichi successivamente non dovrà conoscere l'intera base di codice al rovescio. A volte potrebbe anche essere l'autore originale (tempo che passa, mancanza di sonno ...). Gli imbrogli potrebbero essere gli studenti a cui viene dato il codice di scheletro per dare una svolta, e cambiando logica che non dovrebbero toccare.
Phil Lello,

0

I metodi / variabili privati ​​saranno generalmente nascosti da una sottoclasse. Questa può essere una buona cosa.

Un metodo privato può fare ipotesi sui parametri e lasciare il controllo di integrità al chiamante.

Un metodo protetto dovrebbe controllare gli input di integrità.


-1

"privato" significa: non inteso per essere modificato o accessibile da chiunque tranne la classe stessa. Non inteso per essere modificato o accessibile da sottoclassi. Sottoclassi? Quali sottoclassi? Non dovresti sottoclassarlo!

"protetto" significa: destinato a essere modificato o accessibile solo da classi o sottoclassi. Probabile deduzione che dovresti sottoclassare, altrimenti perché "protetto" e non "privato"?

C'è una chiara differenza qui. Se faccio qualcosa di privato, dovresti tenere lontane le dita sporche. Anche se sei una sottoclasse.


-1

Una delle prime cose che faccio quando eseguo una sottoclasse di una classe è quella di cambiare un gruppo di metodi privati ​​in protetti

Alcuni ragionamenti sui metodiprivate vs .:protected

privatei metodi impediscono il riutilizzo del codice. Una sottoclasse non può utilizzare il codice nel metodo privato e potrebbe essere necessario implementarlo di nuovo - o implementare nuovamente i metodi che originariamente dipendono dal metodo privato ecc.

D'altra parte, qualsiasi metodo che non è privatepuò essere visto come un'API fornita dalla classe al "mondo esterno", nel senso che anche le sottoclassi di terze parti sono considerate "mondo esterno", come suggerito da qualcun altro nella sua risposta già.

È una brutta cosa? - Io non la penso così.

Naturalmente, un'API pubblica (pseudo-) blocca il programmatore originale e impedisce il refactoring di quelle interfacce. Ma visto il contrario, perché un programmatore non dovrebbe progettare i propri "dettagli di implementazione" in modo pulito e stabile come la sua API pubblica? Dovrebbe usare in privatemodo da poter essere sciatto sulla strutturazione del suo codice "privato"? Pensando forse che avrebbe potuto ripulirlo più tardi, perché nessuno se ne accorgerà? - No.

Il programmatore dovrebbe anche riflettere un po 'sul suo codice "privato", per strutturarlo in modo da consentire o addirittura promuovere il riutilizzo di quanto più possibile in primo luogo. Quindi le parti non private potrebbero non diventare tanto un peso in futuro quanto un po 'di paura.

Un sacco di codice (quadro) che vedo adotta un uso incoerente di private: protectedsi trovano comunemente metodi non finali che fanno a malapena qualcosa di più che delegare a un metodo privato. protected, metodi non definitivi il cui contratto può essere adempiuto solo attraverso l'accesso diretto a settori privati.

Questi metodi non possono essere logicamente sovrascritti / migliorati, sebbene tecnicamente non ci sia nulla per renderlo ovvio (compilatore).

Vuoi estendibilità ed eredità? Non fare i tuoi metodi private.

Non vuoi che alcuni comportamenti della tua classe vengano modificati? Crea i tuoi metodi final.

Davvero non può essere chiamato il tuo metodo al di fuori di un determinato contesto ben definito? Rendi il tuo metodo privatee / o pensa a come rendere il contesto ben definito richiesto disponibile per il riutilizzo attraverso un altro protectedmetodo wrapper.

Ecco perché sostengo di usare privatecon parsimonia. E di non confondere privatecon final. - Se l'implementazione di un metodo è vitale per il contratto generale della classe e quindi non deve essere sostituita / ignorata, fallo final!

Per i campi, privatenon è proprio male. Fintanto che i campi possono essere ragionevolmente "usati" con metodi appropriati ( no getXX() o nosetXX() !).


2
-1 Questo non affronta la domanda originale privatevs protected, dà consigli errati ("usare privatecon parsimonia"?) E va contro il consenso generale sulla progettazione di OO. L'uso privatenon ha nulla a che fare con il nascondere il codice sciatto.
Andres F.

3
Dici che una classe ha un'API, ma non sembra stabilire la connessione che l'utente dell'API non vuole essere gravato da dettagli che non è necessario conoscere. La situazione ideale per l'utente di una classe è che l'interfaccia contenga solo i metodi di cui ha bisogno e nient'altro. La creazione di metodi privateaiuta a mantenere pulita l'API.
Ben Cottrell,

1
@HannoBinder Sono d'accordo sul fatto che ci sono molte classi là fuori che soffrono di rigidità, tuttavia la flessibilità e il riutilizzo del codice sono molto meno importanti dell'incapsulamento e della manutenibilità a lungo termine. Considera il tuo caso in cui tutte le classi hanno membri protetti e le classi derivate possono sbagliare con i dettagli di implementazione che desiderano; in che modo testate in modo affidabile tali classi sapendo che qualsiasi cosa in qualsiasi parte del codice non ancora scritto potrebbe causare il fallimento di quel codice in qualsiasi momento? quanti "hack" potrebbero prendere il codice prima che degeneri in una grande palla di fango?
Ben Cottrell,

2
Oh, hai detto "membri". Quello che sto dicendo si riferisce solo ai metodi . Le variabili membro (campi) sono una storia diversa, in cui la privacy è molto più giustificata.
JimmyB,

2
La nomenclatura nella discussione è dappertutto perché non è specifica per una lingua particolare. "Membro" in C ++ può essere dato, funzione, tipo o modello.
JDługosz,

-1

Conosci un caso d'uso importante in cui privato invece di protetto è un buon strumento o due opzioni "protette e pubbliche" sarebbero sufficienti per le lingue OOP?

Privato : quando hai qualcosa che non sarà mai utile per qualsiasi sottoclasse da chiamare o sovrascrivere.

Protetto : quando hai qualcosa che ha un'implementazione / costante specifica della sottoclasse.

Un esempio:

public abstract Class MercedesBenz() extends Car {
  //Might be useful for subclasses to know about their customers
  protected Customer customer; 

  /* Each specific model has its own horn. 
     Therefore: protected, so that each subclass might implement it as they wish
  */
  protected abstract void honk();

  /* Taken from the car class. */
  @Override
  public void getTechSupport(){
     showMercedesBenzHQContactDetails(customer);
     automaticallyNotifyLocalDealer(customer);
  }

  /* 
     This isn't specific for any subclass.
     It is also not useful to call this from inside a subclass,
     because local dealers only want to be notified when a 
     customer wants tech support. 
   */
  private void automaticallyNotifyLocalDealer(){
    ...
  }
}

2
quando hai qualcosa che non sarà mai utile per qualsiasi sottoclasse da chiamare o ignorare - E questo è qualcosa che, secondo me, non conosci mai in anticipo.
Adam Libuša,

Bene, potrei anche aver bisogno di premere il pulsante di reset sul mio CMOS. Ma il presupposto predefinito è che non ne avrò davvero bisogno, e quindi viene inserito nello chassis. Lo stesso vale per i metodi. Lo rendi protetto se la sottoclasse deve assolutamente reimplementarla / chiamarla (vedi: clacson). Lo rendi pubblico se altre parti devono chiamarlo.
Arnab Datta,

-2

È stato difficile per me capire questa faccenda, quindi vorrei condividere un pezzo della mia esperienza:

  • Qual è il campo protetto ? E ' niente di più di un campo, che non è possibile accedere al di fuori di una classe, vale a dire pubblicamente come questo: $classInstance->field. E il trucco è "questo è". I bambini della tua classe avranno pieno accesso ad essa, perché è la loro parte interna legittima.
  • Qual è il campo privato ? È un " vero privato " per la tua classe e la tua implementazione personale di questa classe. "Tenere lontano dalla portata dei bambini", proprio come su una bottiglia di medicina. Avrai la garanzia che è irreversibile dai derivati ​​della tua classe, i tuoi metodi - quando chiamati - avranno esattamente ciò che hai dichiarato

AGGIORNAMENTO: un esempio pratico di un vero compito che ho risolto. Eccolo: hai un token, come USB o LPT (quello era il mio caso), e hai un middleware. Il token richiede un codice PIN, si apre se è corretto e è possibile inviare una parte crittografata e un numero di chiave per decifrare. Le chiavi sono memorizzate in token, non puoi leggerle, solo usarle. E c'erano chiavi temporanee per una sessione, firmate da una chiave in un token, ma memorizzate in un middleware stesso. La chiave temporanea non doveva fuoriuscire ovunque, solo per esistere a livello di driver. E ho usato un campo privato per memorizzare questa chiave temporanea e alcuni dati relativi alla connessione hardware. Quindi nessun derivato è stato in grado di utilizzare non solo un'interfaccia pubblica, ma anche alcuni protettisubroutine "pratiche" che ho creato per un'attività, ma non sono riuscito ad aprire una cassaforte con le chiavi e l'interazione HW. Ha senso?


scusate ragazzi! li ho appena incasinati - aggiornando la mia risposta =) GRAZIE! Avevo fretta e ho sbagliato a scrivere =)
Alexey Vesnin l'

2
Penso che l'OP sia consapevole delle differenze tra i due livelli di protezione, ma è interessato al motivo per cui uno dovrebbe essere usato rispetto all'altro.
Sam

@ Sam, per favore, leggi meglio la mia risposta. Sono tipi di campo completamente diversi ! l' unica cosa che hanno in comune è che entrambi non possono essere citati pubblicamente
Alexey Vesnin

2
Non sono sicuro di cosa ti riferisca. Sono consapevole delle differenze tra privato e protetto. Forse hai capito male cosa intendevo per livello di protezione? Mi riferisco a "livelli di accesso". Stavo cercando di sottolineare che, sebbene la tua risposta non sia negativa, non affronta direttamente la domanda. L'OP sembra sapere cosa significano sia protetti / privati ​​/ pubblici, ma non è sicuro in quali circostanze vorrebbero scegliere l'uno rispetto all'altro. Questo potrebbe essere il modo in cui sei stato votato (non da me).
Sam

@Sam Penso di avere il tuo punto - aggiunto un caso pratico dalla mia pratica / esperienza reale. Cosa ti manca?
Alexey Vesnin,
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.