7

Al mio vecchio lavoro C++, abbiamo sempre avuto molta cura nell'incapsulare le variabili membro e nell'esporre solo come proprietà quando assolutamente necessario. Avremmo costruttori davvero specifici che si sono assicurati di aver completamente costruito l'oggetto prima di usarlo.Incapsulamento nell'era dei quadri

In questi giorni, con i framework ORM, l'iniezione delle dipendenze, la serializzazione, ecc., Sembra che tu stia meglio facendo affidamento sul costruttore predefinito ed esponendo tutto sulla tua classe nelle proprietà, in modo da poter iniettare cose, oppure costruisci e popola gli oggetti in modo più dinamico.

In C#, è stato fatto un passo avanti con gli inizializzatori di oggetti, che ti danno la possibilità di definire fondamentalmente il tuo costruttore. (So ​​che gli inizializzatori di oggetti non sono realmente costruttori personalizzati, ma spero che tu capisca il mio punto.)

Ci sono problemi generali in questa direzione? Sembra che l'incapsulamento stia iniziando a diventare meno importante a favore della convenienza.

EDIT: So che puoi ancora incapsulare attentamente i membri, ma mi sento come quando stai provando a sfornare alcune classi, o devi sederti e pensare attentamente a come incapsulare ciascun membro, o semplicemente esporlo come una proprietà, e preoccuparsi di come viene inizializzato in seguito. Sembra proprio che l'approccio più semplice in questi giorni sia quello di esporre le cose come proprietà e non essere così attenti. Forse ho sbagliato solo, ma questa è stata solo la mia esperienza, soprattutto con le nuove funzionalità del linguaggio C#.

+0

Non farti ingannare da nessuno, quegli inizializzatori dell'oggetto sono solo zucchero sintattico. – BobbyShaftoe

+0

So che gli inizializzatori di oggetti non sono realmente costruttori, ma non è questo il mio punto. Permettono di costruire un oggetto usando il costruttore predefinito e impostando le proprietà a mano. –

+0

Non proprio sicuro di quale sia la differenza tra questi e semplicemente digitando ogni istruzione per impostare ciascuna proprietà. Ma ok. – BobbyShaftoe

risposta

3

Non sono d'accordo con la vostra conclusione. Esistono molti buoni modi per incapsulare in C# con tutte le tecnologie sopra citate, al fine di mantenere buone pratiche di codifica del software. Direi anche che dipende dalla demo della tecnologia che stai guardando, ma alla fine si riduce a ridurre lo spazio degli stati dei tuoi oggetti in modo che tu possa assicurarsi di tenere sempre le loro invarianti.

Take framework relazionali oggetto; la maggior parte di questi ti consente di specificare come stanno andando ad idratare le entità; Per esempio, NHibernate ti consente di dire access="property" o access="field.camelcase" e simili. Questo ti permette di incapsulare le tue proprietà.

L'iniezione di dipendenza funziona sugli altri tipi che hai, principalmente quelli che non sono entità, anche se puoi combinare AOP + ORM + IOC in alcuni modi molto carini per migliorare lo stato di queste cose. IoC è spesso usato da strati sopra le entità di dominio se stai costruendo un'applicazione basata sui dati, che suppongo tu sia, dal momento che stai parlando di ORM.

Essi ("loro" sono applicazioni e servizi di dominio e altre classi intrinseche al programma) espongono le loro dipendenze ma in realtà possono essere incapsulati e testati in un isolamento ancora migliore rispetto al passato dai paradigmi di design per contratto/design -by-interface che usi spesso quando fai il mocking delle dipendenze nei test di simulazione (in congiunzione con IoC), ti sposterà verso la "semantica" di classe come componente. Voglio dire: ogni classe, una volta costruita utilizzando quanto sopra, sarà meglio incapsulata.

aggiornato per urig: Questo vale sia per i esponendo concreti dipendenze e interfacce esponendo. Prima di tutto sulle interfacce: ciò di cui mi accenno sopra era che i servizi e le altre classi di applicazioni che hanno dipendenze, possono con OOP dipendere da contratti/interfacce piuttosto che da implementazioni specifiche. In C/C++ e nelle lingue più vecchie non c'era l'interfaccia e le classi astratte non potevano che andare così lontano. Le interfacce consentono di associare diverse istanze di runtime alla stessa interfaccia senza doversi preoccupare di perdite di stato interno, ovvero di ciò che si sta tentando di allontanarsi durante l'astrazione e l'incapsulamento. Con le classi astratte è ancora possibile fornire un'implementazione di classe, solo che non è possibile creare un'istanza, ma gli eredi hanno ancora bisogno di conoscere gli invarianti nella tua implementazione e questo può rovinare lo stato.

In secondo luogo, sulle classi concrete come proprietà: devi essere cauto su quali tipi di tipi;) esponi come proprietà. Di 'che hai una lista nella tua istanza; quindi non esporre IList come proprietà; questo probabilmente cola e non è possibile garantire che i consumatori dell'interfaccia non aggiungano cose o rimuovano le cose dalle quali si dipende; esporre invece qualcosa come IEnumerable e restituire una copia dell'elenco o, ancora meglio, farlo come metodo: public IEnumerable MyCollection {get {return _List.Enum(); }} e puoi essere sicuro al 100% di ottenere sia le prestazioni che l'incapsulamento. Nessuno può aggiungere o rimuovere questo oggetto IEnumerable e non è ancora necessario eseguire una costosa copia dell'array. Il metodo di supporto corrispondente:

static class Ext { 
    public static IEnumerable<T> Enum<T>(this IEnumerable<T> inner) { 
     foreach (var item in inner) yield return item; 
    } 
} 

Così, mentre non si può ottenere il 100% incapsulamento in diciamo creando sovraccarico uguale operatori/metodo è possibile avvicinarsi con le interfacce pubbliche.

È inoltre possibile utilizzare le nuove funzionalità di .Net 4.0 basate su SpeC# per verificare i contratti di cui sopra.

Serializzazione sarà sempre presente e lo è stato per molto tempo. Precedentemente, prima che l'area internet venisse utilizzata per salvare il tuo oggetto grafico su disco per il successivo recupero, ora è utilizzato nei servizi Web, nella semantica della copia e quando si passano dati ad es. un browser. Ciò non interrompe necessariamente l'incapsulamento se si inseriscono alcuni attributi [NonSerialized] o gli equivalenti nei campi corretti.

Gli inizializzatori di oggetti non sono la stessa cosa dei costruttori, sono solo un modo per comprimere alcune righe di codice. I valori/istanze in {} non saranno assegnati fino a quando tutti i costruttori non avranno eseguito, quindi in linea di principio è come non utilizzare gli inizializzatori di oggetti.

Immagino che ciò a cui devi prestare attenzione si discosti dai buoni principi appresi dal tuo precedente lavoro e assicurati di mantenere gli oggetti del dominio pieni di logica aziendale incapsulata dietro buone interfacce e idem per il tuo servizio -strato.

+0

Ciao Henrik, Puoi dare un esempio di come "Espongono le loro dipendenze ma in realtà possono essere incapsulati e testati in un isolamento ancora migliore rispetto a prima"? Se una classe espone le sue dipendenze in proprietà pubbliche, come può non violare l'incapsulamento? Grazie! urig – urig

+0

Tornando indietro, guardando questo, vedo che ero confuso in quello che intendevo. – Henrik

0

Penso che l'incapsulamento sia ancora importante, aiuta di più nelle biblioteche di qualsiasi altra cosa. Puoi creare una libreria che fa X, ma non hai bisogno che tutti sappiano come è stata creata X. E se volessi crearlo in modo più specifico per offuscare il modo in cui crei X. Il modo in cui ho imparato a conoscere l'incapsulamento, ricordo anche che dovresti sempre definire le tue variabili come private per proteggerle da un attacco di dati. Proteggere da un hacker che rompe il codice e accede a variabili che non dovrebbero usare.

1

I membri privati ​​sono ancora incredibilmente importanti. Controllare l'accesso ai dati degli oggetti interni è sempre buono e non dovrebbe essere ignorato.

Molte volte i metodi privati ​​ho trovato eccessivo. Il più delle volte, se il lavoro che stai facendo è abbastanza importante da scoppiare, puoi refactarlo in modo tale che a) il metodo privato sia banale, o b) sia parte integrante di altre funzioni.

Inoltre, con il test delle unità, l'utilizzo di molti metodi privati ​​rende molto difficile il test dell'unità. Ci sono modi per aggirare questo problema (creare oggetti di prova amici, ecc.), Ma aggiungere difficoltà.

Non vorrei comunque escludere completamente i metodi privati.Ogni volta che ci sono degli algoritmi interni importanti che non hanno alcun senso al di fuori della classe, non c'è motivo di esporre quei metodi.