Quando dovresti usare una classe privata / interna?


21

Per chiarire, quello che sto chiedendo è

public class A{
    private/*or public*/ B b;
}

vs.

public class A{
    private/*or public*/ class B{
        ....
    }
}

Posso sicuramente pensare ad alcuni motivi per usare l'uno o l'altro, ma quello che mi piacerebbe davvero vedere sono esempi convincenti che dimostrano che i pro ei contro non sono solo accademici.


2
La risposta dipende dalle strutture fornite dalla lingua e dalle librerie di base. Supponendo che il linguaggio C #, prova a eseguirli con StyleCop. Sono abbastanza sicuro che riceverai un avviso sulla separazione di questa classe nel suo file. Ciò significa che un numero sufficiente di persone in MSFT ritiene che non sia necessario utilizzare uno degli esempi utilizzati.
Giobbe

Quale sarebbe un esempio convincente se gli esempi accademici non fossero abbastanza buoni?

@Job StyleCop avviserà solo se le classi interne sono visibili dall'esterno. Se è privato, è completamente ok e in realtà ha molti usi.
julealgon,

Risposte:


20

In genere vengono utilizzati quando una classe è un dettaglio di implementazione interna di un'altra classe anziché una parte della sua interfaccia esterna. Li ho visti principalmente come classi di soli dati che rendono le strutture di dati interne più pulite in linguaggi che non hanno una sintassi separata per le strutture in stile c. Sono anche utili a volte per oggetti derivati ​​ad un metodo altamente personalizzati come gestori di eventi o thread di lavoro.


2
È così che li uso. Se una classe è, da un punto di vista funzionale, nient'altro che un dettaglio dell'implementazione privata di un'altra classe, allora dovrebbe essere dichiarata in quel modo, proprio come sarebbe un metodo o campo privato. Nessun motivo per inquinare lo spazio dei nomi con immondizia che non verrà mai utilizzata altrove.
Aaronaught il

9

Supponiamo di costruire un albero, un elenco, un grafico e così via. Perché dovresti esporre i dettagli interni di un nodo o di una cella al mondo esterno?

Chiunque utilizzi un grafico o un elenco dovrebbe fare affidamento solo sulla sua interfaccia, non sulla sua implementazione, dal momento che potresti voler cambiarlo un giorno in futuro (ad esempio da un'implementazione basata su array a una basata su puntatore) e i client che utilizzano il tuo la struttura dei dati ( ognuno di essi ) dovrebbe modificare il proprio codice per adeguarlo alla nuova implementazione.

Invece, l'incapsulamento dell'implementazione di un nodo o di una cella in una classe interna privata ti dà la libertà di modificare l'implementazione ogni volta che è necessario, senza che i client siano costretti a modificare il loro codice di conseguenza, finché l'interfaccia della struttura dei dati rimane intatto.

Nascondere i dettagli dell'implementazione della struttura dei dati porta anche a vantaggi in termini di sicurezza, perché se si desidera distribuire la classe si renderà disponibile solo il file di interfaccia insieme al file di implementazione compilato e nessuno saprà se si stanno effettivamente utilizzando array o puntatori per la tua implementazione, proteggendo così la tua applicazione da un qualche tipo di sfruttamento o, almeno, conoscenza dovuta all'impossibilità di abusare o ispezionare il tuo codice. Oltre ai problemi pratici, non sottovalutare il fatto che in questi casi sia una soluzione estremamente elegante.


5

A mio avviso, è un poliziotto equo utilizzare le classi definite internamente per le classi che vengono utilizzate brevemente in una classe per consentire un'attività specifica.

Ad esempio, se è necessario associare un elenco di dati costituito da due classi non correlate:

public class UiLayer
{
    public BindLists(List<A> as, List<B> bs)
    {
        var list = as.ZipWith(bs, (x, y) => new LayerListItem { AnA = x, AB = y});
        // do stuff with your new list.
    }

    private class LayerListItem
    {
        public A AnA;
        public B AB;
    }
}

Se la tua classe interna viene utilizzata da un'altra classe, dovresti metterla a parte. Se la tua classe interna contiene qualche logica, dovresti metterla a parte.

Fondamentalmente, penso che siano fantastici per tappare buchi nei tuoi oggetti dati, ma sono difficili da mantenere se devono contenere effettivamente la logica, poiché dovrai sapere dove cercarli se devi cambiarli.


2
Supponendo C #, non puoi semplicemente usare le tuple qui?
Giobbe

3
@Job Certo, ma a volte tuple.ItemXrende il codice poco chiaro.
Lasse Espeholt,

2
@Job yup, lasseespeholt ha capito bene. Preferirei vedere una classe interna di {string Title; stringa Descrizione; } di una Tupla <stringa, stringa>.
Ed James,

a quel punto non avrebbe senso mettere quella classe nel suo stesso file?
Giobbe

No, non se lo stai usando davvero solo per collegare brevemente alcuni dati per realizzare un compito che non esula dall'ambito della classe genitore. Almeno, non secondo me!
Ed James,

4
  • Le classi interne in alcune lingue (ad esempio Java) hanno un legame con un oggetto della loro classe di contenimento e possono usare i loro membri senza qualificarli. Se disponibile, è più chiaro che reimplementarli utilizzando altre strutture linguistiche.

  • Le classi nidificate in altre lingue (ad esempio C ++) non hanno tale legame. Stai semplicemente usando il controllo di scoping e accessibilità fornito dalla classe.


3
In realtà Java ha entrambi .
Joachim Sauer,

3

Evito le classi interne in Java perché lo scambio a caldo non funziona in presenza di classi interne. Lo odio perché odio davvero compromettere il codice per tali considerazioni, ma quei riavvi di Tomcat si sommano.


questo problema è documentato da qualche parte?
moscerino

Downvoter: la mia affermazione è errata?
Kevin Cline,

1
Ho effettuato il downgrade perché la tua risposta è priva di dettagli, non perché non è corretta. La mancanza di dettagli rende difficile la tua affermazione. Se ne aggiungi di più su questo problema, probabilmente tornerei a votare, perché le tue informazioni sembrano piuttosto interessanti e potrebbero essere utili
moscerino

1
@gnat: ho fatto una piccola ricerca e sebbene questa sia una verità ben nota, non ho trovato un riferimento definitivo sui limiti di Java HotSwap.
Kevin Cline,

non siamo su Skeptics.SE :) solo alcuni riferimenti potrebbero fare, non è necessario che siano definitivi
moscerino

2

Uno degli usi per la classe interna statica privata è il modello Memo. Metti tutti i dati che devono essere ricordati in classe privata e li restituisci da una funzione come Oggetto. Nessuno al di fuori non può (senza riflessione, deserializzazione, ispezione della memoria ...) esaminarlo / modificarlo, ma può restituirlo alla tua classe e ripristinarne lo stato.


2

Un motivo per utilizzare una classe interna privata potrebbe essere perché un'API che stai utilizzando richiede l'ereditarietà di una classe particolare, ma non vuoi esportare la conoscenza di quella classe agli utenti della tua classe esterna. Per esempio:

// async_op.h -- someone else's api.
struct callback
{
    virtual ~callback(){}
    virtual void fire() = 0;
};

void do_my_async_op(callback *p);



// caller.cpp -- my api
class caller
{
private :
    struct caller_inner : callback
    {
        caller_inner(caller &self) : self_(self) {}
        void fire() { self_.do_callback(); }
        caller &self_;
    };

    void do_callback()
    {
        // Real callback
    }

    caller_inner inner_;

public :
    caller() : inner_(*this) {}

    void do_op()
    {
        do_my_async_op(&inner_);
    }
};

In questo, do_my_async_op richiede che gli venga passato un oggetto di tipo callback. Questo callback ha un firma della funzione membro pubblico utilizzata dall'API.

Quando do_op () viene chiamato da outer_class, utilizza un'istanza della classe interna privata che eredita dalla classe di richiamata richiesta anziché un puntatore a se stessa. La classe esterna ha un riferimento alla classe esterna al semplice scopo di deviare il callback nella classe esterna funzione membro privata do_callback .

Il vantaggio è che sei sicuro che nessun altro può chiamare la funzione pubblica "fire ()".


2

Secondo Jon Skeet in C # in Depth, l'uso di una classe nidificata era l'unico modo per implementare un singleton completamente pigro e sicuro per i thread (vedi Quinta versione) (fino a .NET 4).


1
è il linguaggio di Initialization On Demand Holder , in Java richiede anche una classe interna statica privata. È raccomandato nelle FAQ di JMM , da Brian Goetz in JCiP 16.4 e da Joshua Bloch in JavaOne 2008 Java più efficace (pdf) "For High Performance on a Static Field ..."
gnat

1

Le classi private e interne vengono utilizzate per aumentare i livelli di incapsulamento e nascondere i dettagli di implementazione.

In C ++, oltre a una classe privata, lo stesso concetto può essere realizzato implementando lo spazio dei nomi anonimo di una classe cpp. Questo serve a nascondere / privatizzare un dettaglio di implementazione.

È la stessa idea di una classe interna o privata ma a un livello ancora maggiore di incapsulamento in quanto è completamente invisibile al di fuori dell'unità di compilazione del file. Nulla di ciò appare nel file di intestazione o è esternamente visibile nella dichiarazione della classe.


Qualche feedback costruttivo sul -1?
Hiwaylon,

1
Non ho votato a fondo ma immagino che tu non stia davvero rispondendo alla domanda: non stai dando alcun motivo per usare la classe privata / interna. Stai solo descrivendo come funziona e come fare una cosa simile in c ++
Simon Bergot

Infatti. Ho pensato che fosse ovvio dal mio e dalle altre risposte (nascondere i dettagli dell'implementazione, aumentare i livelli di incapsulamento, ecc.) Ma cercherò di chiarire alcuni. Grazie @Simon.
Hiwaylon,

1
Bella modifica. E per favore non offenderti a causa di questo tipo di reazione. La rete SO sta cercando di applicare politiche per migliorare il rapporto segnale rumore. Alcune persone sono piuttosto rigide riguardo all'ambito delle domande / risposte e a volte dimenticano di essere gentili (commentando il loro voto negativo, per esempio)
Simon Bergot,

@Simon Grazie. È deludente a causa della scarsa qualità della comunicazione che mostra. Forse è un po 'troppo facile essere "severi" dietro il velo anonimo del web? Oh beh, niente di nuovo, immagino. Grazie ancora per il feedback positivo.
Hiwaylon,
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.