OOP Object Oriented Programming
-
Programmazione orientata agli oggetti
Alla base dell’ OOP c’è il concetto di oggetto, ma come viene visto l’oggetto nella OOP?
Nella OOP la nozione di oggetto coincide in buona parte con quella che noi abbiamo degli oggetti incapsulati.
Cosa sono gli oggetti incapsulati?
Sono oggetti che esibiscono all’esterno un comportamento, che essi stessi producono, in dipendenza dalle
richieste provenienti dal mondo esterno ad essi.
Una lavastoviglie, una lavatrice, un ferro da stiro, un’automobile sono esempi di oggetti incapsulati, cioè sono
oggetti dei quali noi conosciamo le funzioni, ma non il funzionamento interno, nel senso che sappiamo quale
comportamento (insieme delle funzioni) essi possono assumere, ma non sappiamo con quali meccanismi
interni possa essere implementato (funzionamento); ad una lavastoviglie possiamo chiedere ad esempio di
eseguire il lavaggio completo per una temperatura stabilita, oppure possiamo chiedere di eseguire solo un
lavaggio rapido ancora a una certa temperatura; i due tipi di lavaggio citati fanno parte del comportamento
della lavastoviglie nel senso che costituiscono una parte delle funzioni che possiamo richiedere alla
lavastoviglie, mentre il modo in cui vengono utilizzate le resistenze, le pompe etc.. per realizzare le funzioni
predette costituiscono quei meccanismi che non sono visibili all’esterno.
In modo più formale si può dire che nella OOP
<<Un oggetto può essere definito come un’entità suscettibile di trovarsi in uno fra diversi possibili
stati.>>
Tali stati sono determinati dai valori assunti dalle proprietà (caratteristiche) dell’oggetto.
Per un rettangolo si possono considerare come proprietà :
• base
• altezza
dunque i valori assunti dalle proprietà base e altezza definiscono lo stato di un rettangolo
ad esempio 3.0 e 5.0 potrebbero rappresentare lo stato di un rettangolo.
Lo stato di un oggetto può essere modificato attraverso l’esecuzione di operazioni (o comandi o metodi
dell’oggetto) diretti ad un esecutore associato all’oggetto (o incorporato nell’oggetto).
Non è detto che l’esecuzione di un metodo debba necessariamente modificare lo stato di un oggetto, può
essere che essa si limiti a fornire un risultato.
Ad esempio il metodo area eseguibile da un rettangolo dovrebbe limitarsi a restituire la propria area
calcolata sulla base della dimensione della base e dell’altezza.
Esempio
Di una bottiglia si possono considerare come proprietà:
• quantità di liquido in essa attualmente contenuto, espressa in litri
• capienza massima della bottiglia, espressa in litri
supposto che la capienza massima della bottiglia sia di 1 litro allora
• un possibile stato potrà essere rappresentato dalla coppia (0.5 e 1),
• un altro possibile stato potrà essere (0.75 e 1).
In riferimento all’oggetto bottiglia potremmo pensare a un esecutore, incorporato nella bottiglia, in grado di
eseguire i comandi (ovvero riconoscere all’oggetto i metodi) seguenti:
versa_liquido()
e
acquisisci_liquido()
Per tali metodi si può senza dubbio pensare sia necessario specificare il parametro quantità, per
rappresentare la quantità di liquido che deve essere versata / immessa dalla/nella bottiglia.
In considerazione di ciò, i due metodi prima citati possono essere riscritti nel seguente modo:
versa_liquido(quantità)
e
acquisisci_liquido(quantità)
Tali metodi modificano lo stato dell’oggetto bottiglia.
Si può senz’altro comprendere che un metodo possano esistere delle condizioni di applicabilità, dipendenti dal
valore del parametro e dallo stato dell’oggetto.
E’ ovvio che non è possibile aggiungere in una bottiglia una quantità di liquido superiore alla differenza fra la
capienza massima e la quantità di liquido già presente in essa.
Allo stesso modo, non è possibile richiedere a una bottiglia il cui stato sia rappresentato dalla coppia (.5,1) di
eseguire il metodo versa_liquido(0.75).
E’ quindi necessario costruire il metodo in modo da tale che non compia azioni che possano generare
situazioni critiche oppure, se il controllo dell’applicabilità non viene effettuato all’interno del metodo, se però si
decide di non effettuare il controllo all’interno del metodo allora si renderà necessario condizionarne
l’esecuzione all’effettiva possibilità di esecuzione.
In tale caso dovrà essere data all’esterno la possibilità di conoscere per intero/parzialmente o stato
dell’oggetto.
La OOP prevede l’esistenza di funzioni di osservazioni che possono restituire il valore di una/più/tutte le
proprietà di un oggetto.
In sintesi, la OOP vede un oggetto come una scatola chiusa ed opaca (scatola nera o black box) che
nasconde dentro di sé il suo stato ed i meccanismi per l’esecuzione di eventuali metodi (ovvero incapsula in
sé i metodi usati dall’oggetto per rispondere ai comandi).
Dall’esterno è possibile richiedere a un oggetto di eseguire un determinato metodo, specificando gli eventuali
parametri. Tutti i metodi possono essere richiesti dall’esterno? No, solo quelli che rientrano nell’interfaccia
(insieme dei metodi accessibili dall’esterno), possono essere inclusi nell’interfaccia i metodi che permettono
conoscere/modificare lo stato dell’oggetto.
La richiesta di esecuzione di un metodo può essere effettuata anche da un altro oggetto.
Come nella vita reale, anche nella OOP, perché un oggetto esista occorre che esso sia stato creato, in
generale questo avviene chiedendo l’esecuzione di un metodo particolare, detto metodo costruttore.
Nella OOP, di un oggetto è possibile avere due punti di vista diversi :
• uno interno
• uno esterno
Quello interno è quello dell’implementatore ovvero di colui che “realizza” l’oggetto, proseguendo l’analogia
con gli elettrodomestici, è quello di colui che progetta l’elettrodomestico, tale punto di vista è evidentemente
concreto.
Quello esterno è invece astratto (nel senso che astrae dalla modo in cui l’oggetto è realizzato) e coincide con
quello dell’utilizzatore dell’oggetto, il quale conosce solo ciò che l’oggetto esibisce all’esterno.
Classificazione (Classi di oggetti)
Attraverso un processo di classificazione che ha come obiettivo quello di individuare solo le caratteristiche e i
comportamenti comuni a un insieme di oggetti, astraendo dai dettagli che li differenziano e che rendono ogni
oggetto diverso da un altro, è possibile definire una classe.
Una classe si caratterizza per:
• proprietà
• metodi
che possono essere comuni a più oggetti.
Di fatto una classe è uno schema per produrre oggetti strutturalmente identici, (tutti gli oggetti prodotti
avranno le medesime proprietà e i medesimi metodi), può essere vista come una “ matrice di stampa”,
come una fabbrica di oggetti, che possiede lo schema e la tecnica di produzione.
Si dice istanza di una classe X un qualsiasi oggetto strutturalmente determinato dalla classe X.
Definita una classe X, possono essere create un numero arbitrario di oggetti istanza della classe X.
Nella OOP il programmatore ha la possibilità di definire nuove classi dalle quali istanziare oggetti da utilizzare
nei propri programmi, è chiaro che degli oggetti istanze delle classi da esso stesso definite egli avrà una
visione concreta, mentre degli oggetti istanza di classi definite da altri potrà avere solo una visione astratta.
Il fatto che nella OOP il programmatore possa dichiarare ed utilizzare, nei propri programmi, oggetti
istanze di classe già definite da lui stesso oppure definite da altri, permette di economizzare lo sforzo
necessario nell’attività di programmazione.
Nella OOP possiamo riconoscere alcune parole chiave , che è bene ricordare e fare proprie già da subito:
ATTRIBUTI : insieme dei valori delle proprietà di un oggetto, istanza di una classe, in un determinato istante
di tempo, tali valori determinano lo stato di un oggetto, dunque se cambia anche uno solo attributo di un
oggetto, il suo stato varierà di conseguenza.
COMPORTAMENTO : insieme dei metodi che un oggetto, istanza di una classe, è in grado di eseguire
INTERFACCIA(già citata): insieme dei metodi accessibili dall’esterno
Concetti base della OOP
Ma quali sono i concetti base della OOP? Abbiamo già citato alcuni di essi senza evidenziarli come tali, ora
vediamo l’elenco completo
• Astrazione /classificazione
• Incapsulamento (o information hiding)
• Ereditarietà (o inheritance)
• Polimorfismo
Astrazione
processo logico che porta alla creazione di modelli (classi) generici (senza entrare nella situazione specifica) a
partire dai quali verranno creati oggetti.
Incapsulamento
è la proprietà per cui un oggetto contiene ("incapsula") al suo interno proprietà e metodi. E’ una tecnica
utilizzata per nascondere metodi e proteggere gli attributi da accessi diretti non desiderati.
Ereditarità
è la capacità di una classe,
classe, definita come classe genitore di tramandare la propria natura a classi
discendenti,
discendenti, le classi discendenti avranno tutti le proprietà e i metodi, eventualmente modificati, e le
funzioni di osservazioni della classe genitore ed altre proprietà e metodi propri della sola classe
discendente.
Grazie all’ereditarietà è possibile creare una nuova classe che estende un classe già esistente e per questa
è possibile:
• introdurre nuovi comportamenti
• modificare i comportamenti esistenti
Ovviamente questo processo di estensione non provoca alcuna modifica nella classe genitore, che rimane
inalterata.
La classe genitore viene spesso chiamata anche superclasse o classe base mentre la classe discendente
viene spesso chiamata anche sottoclasse o classe derivata.
derivata.
Dalla classe Bottiglia, classe dalla quale abbiamo istanziato l’oggetto bottiglia utilizzato in un esempio
precedente, possiamo derivare le classi BottigliaConTappo e BottigliaSenzaTappo.
Tali classi ereditano tutte le proprietà e i metodi della classe Bottiglia, e la classe BottigliaConTappo può
aggiungere ai metodi ereditati dalla classe il metodo togli_tappo e il metodo metti_tappo.
Dalla classe BottigliaConTappo possiamo poi derivare le classi BottigliaConTappoAVite e la classe
BottigliaConTappoAPressione, esse ereditano tutte le proprietà (anche quelli derivati da Bottiglia) e tutti i
metodi della classe di BottigliaConTappo e dunque anche il metodo togli_tappo e metti_tappo, che
ovviamente dovranno essere realizzati in modo diverso nelle due sottoclassi.
La gerarchia descritta può essere graficamente rappresentata mediante un diagramma ad albero:
Bottiglia
BottigliaSenzaTappo BottigliaConTappo
BottigliaConTappoAVite BottigliaConTappoAPressione
Si parla di ereditarietà semplice quando una classe discendente eredita da una sola classe genitore, si dice
multipla quando eredita da più classi.
La gerachia Bottiglia, BottigliaConTappo, etc … presenta solo ereditarietà semplice
Polimorfismo
per polimorfismo da poli (=molti) + morfismo(=forma/aspetto), nella OOP, si intende la capacità di un oggetto,
istanza di una classe che ha discendenti, di poter assumere tutte le forme caratteristiche dei propri
discendenti o comunque, più in generale, la possibilità di utilizzare un metodo con lo stesso nome su
oggetti istanze di classi diverse, in uno stesso programma, il metodo eseguito dal singolo oggetto
sarà quello definito per la classe della quale il singolo oggetto istanza.
Supposto di aver creato un oggetto istanza della classe BottigliaConTappoAVite e un oggetto della classe e
BottigliaConTappo sarà possibile chiedere a entrambi di eseguire il metodo togli_tappo, ovviamente la
modalità di esecuzione sarà diversa e sarà legata alla classe della quale l’oggetto è istanza, all’atto della
chiamata del metodo sarà richiamato cioè il metodo opportuno, senza la necessità di alcun controllo da parte
dello sviluppatore di software.
Un oggetto non manipola mai direttamente i dati interni (le proprietà) di un altro oggetto ma ogni tipo di
comunicazione tra oggetti deve sempre essere gestita tramite l'uso di messaggi,
messaggi ovvero tramite le chiamate
ai metodi che un oggetto espone all'esterno (cioè quelli che fanno parte dell’interfaccia).
In altre parole un messaggio è una sollecitazione che proviene all’oggetto dall’esterno e che scatena
l’esecuzione di un azione, ovvero di un metodo
La programmazione ad oggetti rappresenta quindi un modo diverso di pensare le applicazioni.
Ogni applicazione è composta da un certo numero di oggetti, ognuno dei quali è indipendente dagli altri ma
comunica con gli altri attraverso lo scambio di messaggi.
Quali sono i vantaggi della OOP?
Sicuramente una grande flessibilità e enorme potenza di utilizzo: si costruiscono i componenti una sola volta
e si possono riutilizzarli successivamente ogniqualvolta ce ne sia bisogno.
Lo scrivere codice all’interno di classi diverse, fornisce un elevato numero di vantaggii:
• Facilità di lettura e di comprensione: Il codice sorgente relativo a una classe oggetto può essere
letto e compreso più facilmente anche da persone diverse dall’autore.
• Economia del tempo impiegato nell’attività di programmazione:
• se è già disponibile una classe (anche realizzata da un altro sviluppatore software) utilizzabile
per realizzare un programma, è possibile istanziare oggetti da quella classe nel proprio programma
senza riscrivere tutto il codice
• grazie all’ereditarietà è possibile evitare di riscrivere nelle sottoclassi il codice già presente nella
superclasse
• Riduzione del tempo necessario per la manutenzione del programma nel tempo per correzioni,
aggiornamenti , miglioramenti. L’ereditarietà incide notevolmente su questo aspetto: ogni modifica di una
superclasse sarà ereditato dalle sue sottoclassi, sia in termini di proprietà, sia in termini di metodi.
• Facilità di sostituzione: Se un particolare classe presenta problema, questa potrà essere in blocco
rimossa e sostituita da un altra.