Perché il codice pulito suggerisce di evitare variabili protette?

161

Pulisci codice suggerisce di evitare variabili protette nella sezione "Distanza verticale" del capitolo "Formattazione":

Concepts that are closely related should be kept vertically close to each other. Clearly this rule doesn't work for concepts that belong in separate files. But then closely related concepts should not be separated into different files unless you have a very good reason. Indeed, this is one of the reasons that protected variables should be avoided.

Qual è il ragionamento?

    
posta Matsemann 28.08.2012 - 17:04
fonte

10 risposte

270

Le variabili protette dovrebbero essere evitate perché:

  1. Tendono a generare problemi YAGNI . A meno che tu non abbia una classe discendente che effettivamente fa cose con il membro protetto, rendila privata.
  2. Tendono a generare problemi LSP . Le variabili protette generalmente hanno qualche invarianza intrinseca ad esse associata (altrimenti sarebbero pubbliche). Gli eredi devono quindi mantenere tali proprietà, che le persone possono rovinare o violare volontariamente.
  3. Tendono a violare OCP . Se la classe base fa troppe ipotesi sul membro protetto, o l'ereditario è troppo flessibile con il comportamento della classe, può portare al comportamento della classe base 'modificato da quell'estensione.
  4. Tendono a condurre all'eredità per estensione piuttosto che per composizione. Questo tende a portare all'accoppiamento più stretto, a più violazioni del SRP , a test più difficili ea un gran numero di altre cose che rientrano la discussione "favorisci la composizione sull'ereditarietà".

Ma come vedi, tutti questi sono "tendono a". A volte un membro protetto è la soluzione più elegante. E le funzioni protette tendono ad avere meno di questi problemi. Ma ci sono un certo numero di cose che causano loro di essere trattati con cura. Con tutto ciò che richiede quel tipo di assistenza, le persone commettono errori e nel mondo della programmazione ciò significa bug e problemi di progettazione.

    
risposta data 28.08.2012 - 18:02
fonte
52

È davvero la stessa ragione per cui eviti i globals, solo su scala più piccola. È perché è difficile trovare ovunque una variabile venga letta, o peggio, scritta e difficile da rendere coerente l'uso. Se l'utilizzo è limitato, come se fosse scritto in uno posto ovvio nella classe derivata e letto in uno posto ovvio nella classe base, le variabili protette possono rendere il codice più chiaro e conciso. Se sei tentato di leggere e scrivere una variabile willy-nilly su più file, è meglio incapsularlo.

    
risposta data 28.08.2012 - 18:06
fonte
26

Non ho letto il libro, ma posso dare una pugnalata a ciò che Zio Bob intendeva.

Se metti protected su qualcosa, significa che una classe può ereditarlo. Ma le variabili membro dovrebbero appartenere alla classe in cui sono contenute; questo fa parte dell'incapsulamento di base. Mettere protected su una variabile membro interrompe l'incapsulamento perché ora una classe derivata ha accesso ai dettagli di implementazione della classe base. È lo stesso problema che si verifica quando si crea una variabile public su una classe ordinaria.

Per correggere il problema, puoi incapsulare la variabile in una proprietà protetta, in questo modo:

protected string Name
{
    get { return name; }
    private set { name = value; }
}

Ciò consente a name di essere impostato in modo sicuro da una classe derivata utilizzando un argomento costruttore, senza esporre i dettagli di implementazione della classe base.

    
risposta data 28.08.2012 - 18:05
fonte
17

L'argomento dello zio Bob è principalmente di distanza: se hai un concetto che è importante per una classe, raggruppa il concetto insieme alla classe in quel file. Non separato su due file sul disco.

Le variabili membro protette sono sparse in due punti e, in qualche modo, sembrano magiche. Tu fai riferimento a questa variabile, ma non è definita qui ... dove è è definito? E così inizia la caccia. Meglio evitare protected del tutto, il suo argomento.

Ora non credo che la regola debba essere sempre rispettata alla lettera (come: non usare protected ). Guarda lo spirito di ciò che sta ottenendo ... raggruppa le cose correlate in un unico file - usa tecniche di programmazione e caratteristiche per farlo. Ti raccomando di non sovra-analizzare e di rimanere coinvolto nei dettagli su questo.

    
risposta data 28.08.2012 - 23:23
fonte
9

Alcune ottime risposte già qui. Ma una cosa da aggiungere:

Nella maggior parte dei moderni linguaggi OO è buona abitudine (in Java AFAIK è necessario) inserire ogni classe in un proprio file (per favore non ottenere dubbi sul C ++ in cui spesso si hanno due file).

Quindi è praticamente impossibile mantenere le funzioni in una classe derivata accedendo a una variabile protetta di una classe base "verticalmente chiusa" nel codice sorgente alla definizione della variabile. Ma idealmente dovrebbero essere tenuti lì, dal momento che le variabili protette sono intese per uso interno, il che rende le funzioni che le accolgono spesso "un concetto strettamente correlato".

    
risposta data 28.08.2012 - 18:44
fonte
4

L'idea di base è che un "campo" (variabile a livello di istanza) dichiarato come protetto sia probabilmente più visibile di quanto debba essere e meno "protetto" di quanto si possa desiderare. Non esiste un modificatore di accesso in C / C ++ / Java / C # equivalente a "accessibile solo da classi figlio all'interno dello stesso assembly", garantendo così la possibilità di definire i propri figli che possono accedere al campo nell'assembly, ma non consentire ai bambini creati in altri assembly lo stesso accesso; C # ha modificatori interni e protetti, ma combinarli rende l'accesso "interno o protetto", non "interno e protetto". Quindi, un campo protetto è accessibile da qualsiasi bambino, indipendentemente dal fatto che tu abbia scritto quel bambino o qualcun altro. Protetto è quindi una porta aperta per un hacker.

Inoltre, i campi in base alla loro definizione non hanno praticamente alcuna validità inerente al loro cambiamento. in C # puoi creare una sola lettura, il che rende i tipi di valore effettivamente costanti ei tipi di riferimento che non possono essere reinizializzati (ma comunque molto mutevoli), ma questo è tutto. Come tali, anche protetti, i tuoi figli (di cui non puoi fidarti) hanno accesso a questo campo e possono impostarlo su qualcosa di non valido, rendendo lo stato dell'oggetto incoerente (qualcosa da evitare).

Il modo accettato di lavorare con i campi è renderli privati e accedervi con una proprietà e / o un metodo getter and setter. Se tutti i consumatori della classe hanno bisogno del valore, rendono il getter (almeno) pubblico. Se solo i bambini ne hanno bisogno, proteggi il getter.

Un altro approccio che risponde alla domanda è chiedersi; perché il codice in un metodo figlio ha bisogno della possibilità di modificare direttamente i miei dati di stato? Cosa dice di quel codice? Questa è l'argomento della "distanza verticale" sul suo volto. Se c'è un codice in un bambino che deve alterare direttamente lo stato genitore, allora forse quel codice dovrebbe appartenere al genitore in primo luogo?

    
risposta data 29.08.2012 - 03:35
fonte
3

È una discussione interessante.

Per essere franchi, i buoni strumenti possono attenuare molti di questi problemi "verticali". In effetti, a mio avviso la nozione di "file" è in realtà qualcosa che ostacola lo sviluppo del software - qualcosa su cui sta lavorando il progetto Light Table (http://www.chris-granger.com/2012/04/12/light- tavolo --- a-new-ide-concept /).

Estrai i file dall'immagine e l'ambito protetto diventa molto più attraente. Il concetto protetto diventa più chiaro in lingue come SmallTalk, in cui qualsiasi eredità estende semplicemente il concetto originale di una classe. Dovrebbe fare tutto, e avere tutto, che la classe genitore ha fatto.

A mio parere, le gerarchie di classi dovrebbero essere il più superficiali possibile, con la maggior parte delle estensioni provenienti dalla composizione. In tali modelli, non vedo alcuna ragione per cui le variabili private per non devono essere protette invece. Se la classe estesa dovrebbe rappresentare un'estensione che includa tutti i comportamenti della classe genitore (che io credo che dovrebbe, e quindi ritengo che i metodi privati siano intrinsecamente cattivi), perché la classe estesa non dovrebbe rappresentare anche l'archiviazione di tali dati?

Per dirla in un altro modo - in ogni caso in cui ritengo che una variabile privata si adatta meglio di una protetta, l'ereditarietà non è la soluzione al problema in primo luogo.

    
risposta data 29.08.2012 - 08:34
fonte
0

Come si dice qui, questo è solo uno dei motivi, cioè, il codice collegato logicamente dovrebbe essere collocato all'interno di entità fisiche collegate (file, pacchetti e altri). Su una scala più piccola questo è lo stesso del motivo per cui non si inserisce una classe che rende le query DB all'interno del pacchetto con classi che visualizzano l'interfaccia utente.

La ragione principale, tuttavia, che le variabili protette non sono raccomandate è perché tendono a interrompere l'incapsulamento. Le variabili e i metodi dovrebbero avere una visibilità il più limitata possibile; per ulteriori riferimenti vedi Java efficace di Joshua Bloch, Articolo 13 - "Minimizza l'accessibilità di classi e membri".

Tuttavia, non dovresti prendere tutto questo ad litteram, solo come una gilda; se le variabili protette sono pessime, non sarebbero state inserite nella lingua in primo luogo. Un posto ragionevole per i campi protetti imho si trova all'interno della classe base da un framework di test (le classi di test di integrazione lo estendono).

    
risposta data 28.08.2012 - 22:43
fonte
0

Trovo utile utilizzare le variabili protette per i test di JUnit. Se li rendi privati, non puoi accedervi durante i test senza riflettere. Questo può essere utile se stai testando un processo complesso attraverso molti cambiamenti di stato.

Potresti renderli privati, con metodi getter protetti, ma se le variabili verranno utilizzate solo esternamente durante un test di JUnit, non sono sicuro che sia una soluzione migliore.

    
risposta data 29.08.2012 - 11:07
fonte
-1

Sì, sono d'accordo che è un po 'strano dato che (come ricordo) il libro tratta anche tutte le variabili non private come qualcosa da evitare.

Penso che il problema con il modificatore protetto sia che non solo il membro esposto alle sottoclassi sia reso visibile all'intero pacchetto. Penso che sia questo duplice aspetto che lo zio Bob sta facendo eccezione a qui. Naturalmente, non è sempre possibile aggirare i metodi protetti e quindi la qualifica della variabile protetta.

Penso che un approccio più interessante sia rendere pubblico tutto, che ti costringerà ad avere classi piccole, il che a sua volta rende l'intera metrica della distanza verticale in qualche modo discutibile.

    
risposta data 28.08.2012 - 22:32
fonte

Leggi altre domande sui tag