Astrazione (informatica)
# Astrazione (informatica)

L'astrazione, in informatica, è l'applicazione del metodo logico di astrazione nella strutturazione della descrizione dei sistemi informatici complessi, per facilitarne la progettazione e manutenzione o la stessa comprensione.

## Descrizione generale

La pratica consiste nel presentare il sistema, ad esempio un pezzo di codice sorgente o uno scambio di trasmissioni di dati, in maniera ridotta ai soli dettagli considerati essenziali all'interesse specifico, ad esempio raggruppando il codice in una funzione o formalizzando un protocollo di comunicazione.

Indica quanto il codice scritto in un linguaggio di programmazione si distacca dalle istruzioni in linguaggio macchina che ad esso corrisponderanno dopo l'operazione di compilazione. Delle istruzioni scritte in Java, per esempio, sono molto più vicine al linguaggio comprensibile all'uomo piuttosto che a quello comprensibile dalla macchina (alto livello di astrazione). Viceversa delle istruzioni scritte in Assembly sono abbastanza vicine (ma non uguali) alle istruzioni in formato comprensibile alla macchina (basso livello di astrazione).

La seguente definizione di astrazione aiuta a capire come questo termine viene applicato all'informatica:

«Astrazione - Un concetto o un'idea non associata a nessuna istanza specifica.»

Il concetto è originato mediante l'analogia con l'astrazione in matematica. La tecnica matematica dell'astrazione comincia con definizioni matematiche; Ad esempio, sia in Informatica che in Matematica i numeri sono concetti nei linguaggi di programmazione. I dettagli dell'implementazione dipendono dall'hardware e dal software, ma non è una restrizione, poiché il concetto di numero in Informatica è ancora basato sul concetto matematico.

In programmazione, l'astrazione può essere applicata a strutture di controllo o a strutture di dati.

* L'astrazione di strutture di controllo implica l'uso di sottoprogrammi e di altri concetti relativi a queste.
* Le strutture di dati astratte permettono di maneggiare i dati in maniere significanti.

Il fatto che i programmatori utilizzino le astrazioni quando possibile per evitare duplicazioni (solitamente di codice) è noto come principio di astrazione.

L'informatica prevalentemente opera indipendentemente dal mondo reale: l'hardware implementa un modello di computazione che è intercambiabile con altri, il software è strutturato in architetture per permettere all'uomo di creare enormi sistemi concentrandosi su pochi problemi alla volta. Queste architetture sono fatte da precise scelte di astrazioni.

Una forma principale di astrazione in Informatica è l'astrazione dei linguaggi: nuovi linguaggi artificiali vengono sviluppati per esprimere specifici aspetti di un sistema. I linguaggi di modellazione aiutano nell'organizzazione. I linguaggi di programmazione possono essere processati con un computer. Un esempio di questo processo di astrazione è lo sviluppo di linguaggi di programmazione dal linguaggio macchina, al linguaggio assembly e ai linguaggi ad alto livello. Ognuno di questi stadi può essere utilizzato come base per lo stadio successivo.

In un linguaggio di programmazione, alcune funzioni permettono al programmatore di creare nuove astrazioni. Queste includono le subroutine, i moduli e le componenti software.

Alcune astrazioni provano a limitare la larghezza dei concetti di cui un programmatore ha bisogno nascondendo completamente le astrazioni sulle quali a loro volta sono costruite. Joel Spolsky, comunque, afferma che nessuna astrazione può completamente nascondere tutti i dettagli: ad ogni modo, questo non nega l'utilità dell'astrazione. Alcune astrazioni sono designate per interagire con altre, ad esempio un linguaggio di programmazione potrebbe contenere una FFI (Foreign Function Interface) per effettuare chiamate a linguaggi di basso livello.

## Caratteristiche dei linguaggi

### Linguaggi di programmazione

Diversi linguaggi di programmazione offrono diversi tipi di astrazione, in base alle applicazioni destinate per il linguaggio. Ad esempio:

* Nei linguaggi di programmazione orientati agli oggetti (OOP - Object-Oriented Programming) come il C++ o Java, il concetto di astrazione è diventato un'istruzione dichiarativa utilizzando la parola chiave "virtual" in C++, e "abstract" in Java. Dopo questa dichiarazione, è compito del programmatore implementare una classe per istanziare l'oggetto della dichiarazione.
* I linguaggi di programmazione funzionale solitamente espongono astrazioni relative a funzioni, come le lambda astrazioni e le funzioni di ordine superiore.
* Il linguaggio Linda permette l'astrazione di alcuni concetti come quello di server per facilitare la programmazione distribuita.

### Metodi di specificazione

Gli analisti hanno sviluppato diversi metodi per specificare formalmente sistemi software. Alcuni metodi conosciuti includono:

* Metodi basati su modelli astratti (VDM, Z);
* Tecniche Algebriche (Larch, CLEAR, OBJ, ACT ONE);
* Tecniche basate su processi (LOTOS, SDL, Estelle);
* Tecniche basate su tracce (SPECIAL, TAM);
* Tecniche basate sulla conoscenza (Refine, Gist).

### Linguaggi di specificazione

I linguaggi di specificazione spesso fanno affidamento su un tipo di astrazione o un altro, visto che le specificazioni sono tipicamente definite all'inizio di un progetto (ad un livello più astratto). Il linguaggio UML, ad esempio, permette di definire classi astratte, che rimangono astratte durante l'architettura e la fase di specificazione del progetto.

## Strutture di controllo astratte

I linguaggi di programmazione permettono astrazioni di strutture di controllo come uno dei loro compiti principali. I computer eseguono operazioni ad un livello molto basso, ad esempio muovendo alcuni bit da una locazione di memoria ad un'altra, producendo la somma di due sequenze di bit. I linguaggi di programmazione permettono che questo sia fatto ad un livello più alto. Consideriamo ad esempio questo codice scritto in Pascal: "a := (1 + 2) * 5;". Per un uomo, questo sembra un semplice e ovvio calcolo ("uno più due fa tre, per cinque fa quindici"). Ebbene, i passaggi a basso livello necessari per arrivare a questa conclusione, ritornare il valore "15" e assegnarlo alla variabile "a", sono più complessi. I valori devono essere convertiti in rappresentazione binaria (un processo più complicato di quanto si possa pensare) e i calcoli devono essere scomposti (dal compilatore o dall'interprete) in istruzioni assembly (molto meno intuitive al programmatore). Alla fine, assegnare il valore risultante "15" alla variabile chiamata "a", in modo che possa essere utilizzata successivamente, implica altri passaggi "dietro le quinte", come cercare la locazione di memoria dove inserire il valore "15", e così via.

Senza questo tipo di astrazione, un programmatore dovrebbe specificare tutti i passaggi "registro/livello binario" anche solo per aggiungere o moltiplicare due numeri e assegnare il risultato ad una variabile. Questo tipo di compito ha due serie conseguenze negative:

* Forza il programmatore a ripetere costantemente le stesse banali e semplici istruzioni ogni volta che ce ne sia bisogno
* Forza il programmatore a programmare per uno specifico set di hardware e istruzioni (si parla quindi di linguaggi legati alla macchina)

### Programmazione strutturata

La programmazione strutturata implica la suddivisione di complessi compiti di un programma in pezzi più piccoli con lo scopo di semplificarli ed evitare "effetti collaterali".

In un programma semplice, questo potrebbe servire per assicurarsi che un ciclo abbia un singolo o più ovvi punti di uscita e, dove possibile, per assicurarsi che funzioni e procedure ne abbiano solo uno.

In un sistema più grande, invece, potrebbe implicare la suddivisione di compiti complessi in tanti moduli diversi. La programmazione orientata agli oggetti (OOP) ha abbracciato ed esteso questo concetto.

## Strutture di dati astratte

Le strutture di dati astratte rinforzano una chiara separazione tra le proprietà astratte di un tipo di dati e i dettagli concreti della sua implementazione. Le proprietà astratte sono quelle che sono visibili a colui che fa uso del tipo di dati - l'interfaccia del tipo di dati - mentre l'implementazione concreta è completamente mantenuta privata, e ovviamente può cambiare per implementare migliorie di efficienza nel tempo. L'idea è che questi cambiamenti non devono avere alcun impatto, e non devono creare differenze nell'aspetto astratto.

Per esempio, si potrebbe definire un tipo di dato astratto chiamato tavola di ricerca (lookup table), che associa in modo univoco chiavi a valori, e nella quale è possibile ottenere il valore desiderato digitando la chiave corrispondente. Una tavola di ricerca del genere può essere implementata in vari modi, ad esempio con una hash table (in italiano Tabella Hash).

I linguaggi che implementano le strutture di dati astratte includono Ada e Modula-2.

## Astrazione nella programmazione orientata agli oggetti (OOP)

Nella teoria della programmazione orientata agli oggetti, l'astrazione implica la facilità di definire oggetti che rappresentino "attori" che possono svolgere lavori, riportare e cambiare il loro stato, e comunicare con gli altri oggetti nel sistema. Il termine incapsulamento si riferisce al nascondere i dettagli di stato, ma estendere il concetto di tipo di dato dai primi linguaggi di programmazione, e standardizzare il modo in cui i diversi tipi di dati interagiscono, è l'inizio dell'astrazione. Si parla di polimorfismo quando l'astrazione consente a oggetti di diverso tipo di venire sostituiti. Si parla di ereditarietà quando più tipi o classi vengono strutturati per semplificare un set complesso di relazioni.

Vari linguaggi di programmazione orientata agli oggetti offrono simili agevolazioni per l'astrazione, per supportare tutti insieme una strategia generale di polimorfismo, che implica la sostituzione di un tipo in uno che abbia lo stesso ruolo o uno simile.

Common Lisp Object System (conosciuto anche come CLOS) e self hanno un grande utilizzo del polimorfismo. Oggetti individuali e funzioni sono astratti più flessibilmente in modo da adattarsi meglio all'eredità condivisa funzionale del Lisp.

Anche se alcuni linguaggi offrono strategie alternative per ottenere la stessa astrazione, fondamentalmente non modificano il bisogno di supportare nomi astratti nel codice - tutta la programmazione conta sull'abilità di astrarre verbi, funzioni, strutture di dati, nomi e processi.

Consideriamo ad esempio un frammento di codice Java per rappresentare alcuni comuni "animali" di fattoria ad un livello di astrazione tale da simulare i più semplici aspetti della loro fame e del nutrimento. Definiamo una classe "Animale" per rappresentare entrambi gli stati dell'animale e le sue funzioni:

Con la definizione di cui sopra, si potrebbero creare oggetti di tipo "Animale" e chiamare metodi del genere:

Nell'esempio sopra, la classe "Animale" è un'astrazione usata al posto di un vero animale, "EssereVivente" è un'ulteriore astrazione (in questo caso una generalizzazione) di "Animale".

Se viene richiesta una gerarchia più differenziata di animali - per differenziare, diciamo, quelli che producono latte da quelli che non producono niente e non la carne dopo che hanno finito di vivere - può esserci un livello intermedio di astrazione, come "Animali Lattiero-Caseari" (mucche o capre) che mangiano cibi per produrre buon latte, e "Animali" (suini e manzi) che mangiano cibi per dare la migliore qualità di carne.

Una astrazione del genere rimuoverebbe il bisogno di specificare il tipo di cibo, in modo da concentrarsi invece sulla pianificazione del nutrimento. Le due classi potrebbero essere legate usando ereditarietà o potrebbero essere da sole, e il programmatore potrebbe definire vari gradi di polimorfismo tra i due tipi. Queste agevolazioni tendono a variare drasticamente tra un linguaggio e l'altro, ma in generale ciò che riesce a fare uno può essere fatto con tutti gli altri.

### Design orientato agli oggetti

Le decisioni su cosa rendere astratto e cosa no è diventata la maggiore preoccupazione del design orientato agli oggetti e dell'analisi dei domini - ciò che invero determina le relazioni rilevanti nel mondo reale è ciò che riguarda l'analisi orientata agli oggetti.

In genere, per determinare appropriate astrazioni, bisogna fare piccole decisioni riguardo al campo, determinare quali altri sistemi devono cooperare, e infine fare una dettagliata analisi orientata agli oggetti. Nel nostro esempio di prima, il dominio è il fienile, i maiali e le mucche e i luoghi sono i vincoli ereditari, la dettagliata analisi è che i programmatori devono avere la flessibilità necessaria per nutrire gli animali, e non c'è alcun motivo di inserire il tipo di cibo dentro la classe stessa, e il design è una singola e semplice classe Animale dove sono inseriti i maiali e le mucche, con le stesse funzioni. La decisione di implementare gli Animali Lattiero-Caseari cambierebbe l'analisi dettagliata, ma il dominio e l'analisi delle eredità non cambierebbero - quindi questo è interamente sotto il controllo del programmatore, e ci riferiamo all'astrazione nella programmazione ad oggetti in modo diverso dall'astrazione nei domini o nell'analisi delle eredità.

## Considerazioni

Quando si discute di semantica dei linguaggi di programmazione, metodi formali o interpretazioni astratte, con astrazione ci si riferisce a una meno dettagliata, ma sicura, definizione dei comportamenti di un determinato programma. Per esempio, è possibile osservare solo il risultato finale del programma, a meno che non si considerino tutti i passaggi intermedi dell'esecuzione. L'astrazione è definita per un concreto (più preciso) modello di esecuzione.

L'astrazione potrebbe essere esatta o molto fedele a una proprietà se si risponde egualmente bene alle domande sulla proprietà del modello astratto. Per esempio, se vogliamo sapere qual è il risultato di una espressione matematica che implica solo l'utilizzo di interi, abbiamo solo bisogno di utilizzare operazioni modulo n (una forma familiare di questa astrazione è la prova del nove).

Le astrazioni, comunque, anche se non esatte, devono essere buone. Infatti, potrebbe essere possibile ottenere buone risposte da esse - anche se l'astrazione può semplicemente produrre un risultato di indecidibilità. Per esempio, potremmo rendere astratti gli studenti di una classe, secondo la loro massima e minima età; se viene chiesto se una persona appartiene a quella classe, si potrebbe semplicemente comparare l'età di quella persona con la massima e la minima di quella classe. Se la sua età non rientra in quell'intervallo, sicuramente si può rispondere che quella persona non appartiene a quella classe. Se invece vi rientra, si può solo rispondere "Non lo so".

Il livello di astrazione implementato in un linguaggio di programmazione può influenzare tutta la sua usabilità. La struttura delle Dimensioni Cognitive include il concetto di gradiente di astrazione in un formalismo. Questa struttura permette al designer di un linguaggio di programmazione di studiare gli scambi tra l'astrazione e le altre caratteristiche del design, e come i cambiamenti nell'astrazione influiscano l'usabilità del linguaggio.

Le astrazioni possono dimostrarsi utili quando si lavora con i programmi, poiché particolari proprietà di questi sono essenzialmente indecidibili. Come conseguenza, i metodi automatici per ottenere informazioni sul comportamento dei programmi possono terminare improvvisamente (senza dare un risultato o andando semplicemente in crash) o potrebbero dare false informazioni.

L'astrazione è il concetto principale dell'interpretazione astratta. Il Model checking generalmente prende luogo su versioni astratte dei sistemi studiati.

## Livelli di astrazione

L'informatica presenta comunemente livelli (o meno comunemente, strati) di astrazione, dove ogni livello rappresenta un diverso modello della stessa informazione o processo, ma usa un sistema di espressione che implica un diverso set di oggetti e composizioni che si applicano solo ad un particolare dominio. I livelli più "alti" di astrazione tendono a costruirsi su qualcosa di relativamente concreto, mentre quelli più "bassi" tendono a dare progressivamente una rappresentazione più "granulare". Ad esempio, le porte costruite mediante circuiti elettronici, il linguaggio macchina binario, linguaggi di programmazione basati sul linguaggio binario, applicazioni e sistemi operativi basati su linguaggi di programmazione. Ogni livello è incorporato, ma non determinato, dal livello sotto di esso, rendendolo un linguaggio di descrizione che è in un certo senso autocontenuto.

### Sistemi di database

Poiché molti utenti di sistemi di database non hanno molta familiarità con le strutture di dati, gli sviluppatori di database spesso nascondono le complessità attraverso i seguenti livelli:

Livello fisico: Il livello più basso di astrazione descrive come un sistema immagazzini i dati. Il livello fisico descrive dettagliatamente le strutture di dati a basso livello.

Livello logico: Il livello superiore di astrazione descrive quali dati immagazzina il database, e quali relazioni ci sono tra questi dati. Il livello logico quindi descrive un intero database in termini di piccoli numeri e strutture relativamente semplici. Anche se l'implementazione delle semplici strutture a livello logico potrebbe implicare complesse strutture a livello fisico, gli utilizzatori dei livelli logici non devono preoccuparsi di queste complessità. Gli amministratori dei database, che devono decidere quali informazioni tenere in un database, usano il livello di astrazione logico.

Livello a viste: Il livello più alto di astrazione descrive solo parte dell'intero database. Anche se il livello logico usa struttura più semplici, rimane la complessità a causa della varietà di informazioni immagazzinata in un grande database. Ad alcuni utenti di sistemi di database non servono tutte queste informazioni; invece, hanno solo bisogno di accedere a una parte del database. Il livello di astrazione a viste esiste per semplificare la loro interazione con il sistema. Il sistema potrebbe anche fornire più viste per lo stesso database.

### Architettura a strati

L'abilità nel fornire un design di livelli differenti di astrazione può:

* semplificare considerevolmente il design
* permettere effettivamente di lavorare su vari livelli di astrazione.

L'architettura a strati divide i compiti di una applicazione in più gruppi. È una tecnica utilizzata nel design di software, hardware e comunicazioni nei quali sistemi o componenti del network sono isolati in strati, in modo da poter effettuare cambiamenti su un solo strato senza dover influenzare anche gli altri.

