il modello di riferimento Open Distributed Processing - scritta dalla International Organization for Standardization - definisce i seguenti concetti:
entità: Qualsiasi calcestruzzo o cosa astratta di interesse.
Oggetto: un modello di un'entità. Un oggetto è caratterizzato dal suo comportamento e, dualmente, dal suo stato.
Comportamento (di un oggetto): una raccolta di azioni con un insieme di vincoli su quando possono verificarsi.
Interfaccia: Un'astrazione del comportamento di un oggetto costituito da un sottoinsieme delle interazioni di quell'oggetto insieme a un insieme di vincoli su quando possono verificarsi.
Incapsulamento: la proprietà che le informazioni contenute in un oggetto sono accessibili solo attraverso le interazioni sulle interfacce supportate dall'oggetto.
Questi, lo apprezzerete, sono abbastanza ampi. Vediamo, tuttavia, se la funzionalità di una funzione all'interno di una funzione può essere considerata logicamente da costituire all'incapsulamento in questi termini.
In primo luogo, una funzione è chiaramente un modello di "Cosa di interesse", in quanto rappresenta un algoritmo che tu (presumibilmente) desideri eseguito e che l'algoritmo riguarda un problema che stai cercando di risolvere (e quindi è un modello di esso).
Una funzione ha un comportamento? Certamente: contiene una raccolta di azioni (che potrebbero essere un numero qualsiasi di istruzioni eseguibili) che vengono eseguite con il vincolo che la funzione deve essere chiamata da qualche parte prima che possa essere eseguita. Una funzione non può essere chiamata spontaneamente in qualsiasi momento, senza un fattore causale. Sembra legalese? Scommetti. Ma andiamo avanti, comunque.
Una funzione ha un'interfaccia? Certamente: ha un nome e una collezione di parametri formali, che a loro volta si associano alle istruzioni eseguibili contenute nella funzione in quanto, una volta chiamata una funzione, il nome e l'elenco dei parametri sono intesi per identificare in modo univoco la raccolta di eseguibili le dichiarazioni devono essere eseguite senza che la parte chiamante specifichi tali dichiarazioni effettive.
Una funzione ha la proprietà che le informazioni contenute nella funzione sono accessibili solo attraverso le interazioni sulle interfacce supportate dall'oggetto? Hmm, bene, può.
Poiché alcune informazioni sono accessibili tramite la relativa interfaccia, alcune informazioni devono essere nascoste e inaccessibili all'interno della funzione. (La proprietà che tali informazioni esibiscono è chiamata "nascondere le informazioni", definita da Parnas sostenendo che i moduli dovrebbero essere progettati per nascondere sia le decisioni difficili che le decisioni che potrebbero cambiare.) Quindi quali informazioni sono nascoste all'interno di una funzione?
Per vedere questo, dovremmo prima considerare la scala. È facile affermare che, ad esempio, le classi Java possono essere incapsulate all'interno di un pacchetto: alcune delle classi saranno pubbliche (e quindi saranno l'interfaccia del pacchetto) e alcune saranno private del pacchetto (e quindi nascoste nel pacchetto). . Nella teoria di incapsulamento, le classi formano nodi e le confezioni formano regioni incapsulate, con la totalità che forma un grafo incapsulato; il grafico di classi e pacchetti è chiamato il terzo grafico.
È anche facile affermare che le funzioni (oi metodi) stessi sono incapsulate all'interno delle classi. Ancora una volta, alcune funzioni saranno pubbliche (e quindi faranno parte dell'interfaccia della classe) e alcune saranno private (e quindi informazioni nascoste all'interno della classe).Il grafico delle funzioni e delle classi è chiamato il secondo grafico.
Ora arriviamo alle funzioni. Se le funzioni devono essere un mezzo di incapsulamento, devono contenere alcune informazioni pubbliche ad altre funzioni e alcune informazioni che sono nascoste all'interno della funzione. Quale potrebbe essere questa informazione?
Un candidato ci viene assegnato da McCabe. Nel suo famoso documento sulla complessità ciclomatica, Thomas McCabe descrive il codice sorgente in cui, "Ogni nodo nel grafico corrisponde a un blocco di codice nel programma in cui il flusso è sequenziale e gli archi corrispondono a rami presi nel programma."
Prendiamo il blocco di esecuzione sequenziale di McCabian come unità di informazioni che può essere incapsulata all'interno di una funzione. Poiché il primo blocco all'interno della funzione è sempre il primo e unico blocco garantito da eseguire, possiamo considerare il primo blocco come pubblico, in quanto può essere chiamato da altre funzioni. Tutti gli altri blocchi all'interno della funzione, tuttavia, non possono essere richiamati da altre funzioni (eccetto che nelle lingue che consentono di saltare nelle funzioni a metà del flusso) e quindi questi blocchi possono essere considerati informazioni nascoste all'interno della funzione.
Prendendo queste definizioni (forse un po 'tenue), allora potremmo dire di sì: mettere funzionalità in una funzione costituisce un incapsulamento. L'incapsulamento dei blocchi all'interno delle funzioni è il primo grafico.
C'è un caveate, tuttavia. Prenderesti in considerazione un pacchetto di cui ogni classe era pubblica per essere incapsulata? Secondo le definizioni di cui sopra, supera il test, come si può dire che l'interfaccia al pacchetto (cioè tutte le classi pubbliche) offre effettivamente un sottoinsieme del comportamento del pacchetto ad altri pacchetti. Ma il sottoinsieme in questo caso è l'intero comportamento del pacchetto, in quanto nessuna classe è nascosta. Quindi, nonostante soddisfino in modo soddisfacente le definizioni di cui sopra, riteniamo che non soddisfi lo spirito delle definizioni, poiché sicuramente qualcosa deve essere nascosto dalle informazioni per poter essere rivendicato il vero incapsulamento.
Lo stesso vale per l'esempio che dai. Possiamo certamente considerare n = n + 1 come un singolo blocco di McCabian, dato che (e la dichiarazione di ritorno) sono un flusso sequenziale di esecuzioni. Ma la funzione in cui si inserisce questo contiene solo un blocco, e quel blocco è l'unico blocco pubblico della funzione, e quindi non ci sono blocchi nascosti di informazioni all'interno della funzione proposta. Quindi può soddisfare la definizione di incapsulamento, ma direi che non soddisfa lo spirito.
Tutto questo, ovviamente, è accademico a meno che non si possa dimostrare un vantaggio tale incapsulamento.
Ci sono due forze che motivano l'incapsulamento: il semantico e il logico.
L'incapsulamento semantico indica semplicemente l'incapsulamento basato sul significato dei nodi (per usare il termine generale) incapsulato. Quindi se ti dico che ho due pacchetti, uno chiamato 'animale' e uno chiamato 'minerale', quindi ti do tre classi Cane, Gatto e Capra e chiedi in quali pacchetti queste classi dovrebbero essere incapsulate, quindi, dato senza altre informazioni, avresti perfettamente ragione nel sostenere che la semantica del sistema suggerirebbe che le tre classi fossero incapsulate all'interno del pacchetto "animale" piuttosto che "minerale".
L'altra motivazione per l'incapsulamento, tuttavia, è logica.
La configurazione di un sistema è l'identificazione precisa ed esaustiva di ciascun nodo del sistema e della regione incapsulata in cui risiede; una particolare configurazione di un sistema Java è - al terzo grafico - per identificare tutte le classi del sistema e specificare il pacchetto in cui ogni classe risiede.
Per incapsulare un sistema logicamente significa identificare alcune proprietà matematiche del sistema che dipendono dalla sua configurazione e quindi configurare tale sistema in modo tale che la proprietà venga minimizzata matematicamente.
La teoria di incapsulamento propone che tutti i grafici incapsulati esprimano un numero massimo potenziale di bordi (MPE). In un sistema Java di classi e pacchetti, ad esempio, l'MPE è il numero massimo potenziale di dipendenze del codice sorgente che possono esistere tra tutte le classi di quel sistema. Due classi all'interno dello stesso pacchetto non possono essere nascoste dall'informazione e quindi entrambe possono potenzialmente formare delle responsabilità l'una sull'altra. Due classi private di pacchetti in pacchetti separati, tuttavia, non possono formare dipendenze l'una dall'altra.
La teoria dell'incapsulazione ci dice quanti pacchetti dovremmo avere per un dato numero di classi in modo che l'errore massimo tollerato sia ridotto al minimo. Questo può essere utile perché la debole forma del Principio di Burden afferma che il massimo carico potenziale di trasformare una raccolta di entità è una funzione del numero massimo potenziale di entità trasformate - in altre parole, maggiore è la potenziale dipendenza del codice sorgente tra le tue lezioni, maggiore è il costo potenziale di fare un particolare aggiornamento. Ridurre al minimo l'errore massimo consentito riduce quindi al minimo il costo potenziale degli aggiornamenti.
Dato n classi e un requisito di p classi pubbliche per pacchetto, la teoria di incapsulamento mostra che il numero di pacchetti, r, dovremmo ridurre l'MPE è dato dall'equazione: r = sqrt (n/p).
Questo vale anche per il numero di funzioni che dovreste avere, dato il numero totale, n, di blocchi di McCabian nel vostro sistema. Le funzioni hanno sempre un solo blocco pubblico, come menzionato sopra, e quindi l'equazione per il numero di funzioni, r, da avere nel tuo sistema semplifica: r = sqrt (n).
Certamente, pochi hanno considerato il numero totale di blocchi nel loro sistema quando praticano l'incapsulamento, ma è prontamente fatto a livello di classe/pacchetto. Inoltre, minimizzare l'MPE è quasi del tutto intuitivo: è fatto riducendo al minimo il numero di classi pubbliche e cercando di distribuire uniformemente le classi sui pacchetti (o almeno evitare di avere molti pacchetti con, diciamo, 30 classi e un pacakge di mostri con 500 classi, nel qual caso l'MPE interno di quest'ultimo può facilmente sopraffare l'MPE di tutti gli altri).
L'incapsulamento comporta quindi un equilibrio tra il semantico e il logico.
Tutto molto divertente.
Se la funzione è stata chiamata "AddX" dove X è un valore che al momento ha un valore di 1 ma questo potrebbe cambiare, allora ci sarebbe l'informazione che si nasconde. Il valore specifico da aggiungere sarebbe nascosto nella definizione della funzione. –
Inoltre, le funzioni sicuramente dividono la responsabilità. Ogni funzione si assume la responsabilità di qualcosa che le altre funzioni non fanno. Sì, un determinato insieme di funzioni può essere progettato male in modo che si sovrappongano, ma lo stesso vale per un insieme di classi mal progettato. –
@Serx La funzione addOne esprime "cosa" fa e nasconde "come" lo fa. Quindi questo si qualifica come l'astrazione + l'occultamento delle informazioni. Perché sei così concentrato sugli oggetti? E come afferma Earwicker, le funzioni possono dividere la responsabilità, ma trovo questo un criterio piuttosto vago per l'incapsulamento. – eljenso