OOP: Object Oriented Programming
Oggi parliamo di programmazione ad oggetti, introducendone le caratteristiche principali e come queste sono implementate nel .NET Framework.
Come molti di voi sapranno la programmazione ad oggetti è un paradigma di programmazione adottato dalla stragrande maggioranza dei programmatori a partire dagli anni '90, anni in cui si è cominciato ad abbandonare il paradigma procedurale in cui la definizione dei tipi di dato vedeva separate la struttura del dato dalle procedure che operavano su di esse. A un certo punto si è giustamente pensato: ma se un'entità è caratterizzata da un insieme di informazioni e dalle operazioni che posso realizzare su di esse perchè la loro definizione deve essere separata? Ed ecco a voi nascere il concetto di classe che insieme alle 3 caratteristiche principali della OOP definisce questo nuovo modo di concepire l'organizzazione della soluzione algoritmica ad un problema dato.
INCAPSULAMENTO
Come detto tutto ruota intorno al concetto di classe, che possiamo vedere come il costrutto offerto dal linguaggio per realizzare l'astrazione del concetto di oggetto. In pratica il trucco sta nell'affrontare il problema da risolvere modellandolo come un sistema in cui esistono e vivono un insieme di oggeti che comunicano tra di loro attraverso un'interfaccia bene definita: i METODI della classe! La struttura del dato incapsulato nell'oggetto è invece definita da quelli che vengono chiamati ATTRIBUTI. Una classe è quindi costituita da attributi e metodi, che insieme definiscono un nuovo tipo di dato per l'ambiente in cui vengono definiti. Un oggetto è un'istanza di una data classe.
Due oggetti, istanza di una certa classe, possono differire in un certo istante di tempo per il valore che in quell'istante hanno i suoi attributi, che nel loro insieme definiscono lo STATO dell'oggetto. Il concetto di stato di un oggetto è di fondamentale importanza perchè su di esso di basa uno dei tre principi fondamentali dalla OOP: l'INFORMATION HIDING o INCAPSULAMENTO secondo il quale l'unico modo attraverso cui debba essere possibile modificare tale stato dovrebbe essere l'utilizzo dei metodi della classe. Tecnicamente questo è possibile grazie all'utilizzo di indicatori di visibilità per gli attributi che dovrebbero negarne l'accesso all'utilizzatore della classe. Quindi gli oggetti del sistema comunicano tra di loro attraverso messaggi scambiati grazie all'invocazione dei metodi alterandone lo stato.
In .NET è possibile realizzare una classe utilizzando il costrutto Class in Visual Basic e class in C#, è possibile definire gli attributi dichiarando all'interno delle classi delle variabili del tipo voluto, è possible definire i metodi definendo delle Sub o delle Function in Visual Basic, definendo delle funzioni in C#. Sia per gli attributi che per i metodi e possibile indicare la visibilità che essi devono avere rispetto agli utilizzatori delle classi, gli indicatori possibili sono tre: privato, publico o protetto. Nel primo classe la visibilità è consentita solo all'interno della classe, nel secondo caso e consentita anche agli utilizzatori, nel terzo caso solo alla classe e alle classi che la estendono, concetto quest'ultimo che affronteremo tra poco. In Visual Basic si utilizzano le parole riservate Private, Public e Protected, in C# private, public e protected:


Come potete notare esiste un metodo speciale nella definizione di una classe, chiamato costruttore, che viene automaticamente invocato all'atto dell'istanziazione di una classe: in questo metodo, che in Visual Basic viene identificato dalla Sub New, mentre in C# è un metodo che ha lo stesso nome della classe e non ha valore di ritorno, possiamo eseguire l'inizializzazione dello stato dell'oggetto.
EREDITARIETA'
La seconda caratteristica principale della OOP è l'ereditarietà, caratteristica che permette di estendere una classe esistente aggiungogli funzionalità che non prevedeva, quindi sono non diversamente indicato dal programmatore della classe (con appositi costrutti) è possibile prendere una classe esistente ed estenderla fornendo funzionalità che la classe base non aveva. Consideriamo ad esempio il classico esempio degli utenti di un sistema: un cliente o un fornitore potrebbe essere un utente del sistema quindi anzichè replicare nelle classi fornitore e cliente le funzionalità specifiche e comuni degli utenti possiamo definire le classi cliente e fornitore come specializzazioni della classe utente:


Come potete osservare nei due costruttori abbiamo valorizzato l'attributo protetto _denominazione, la cui visibilità ci è consentita dalla dichiarazione come protected dell'attributo. Questa caratteristica permette il riuso del codice esistente in utente il chè è un bel vantaggio già di per se, ma l'ereditarietà mostra le sue vere potenzialità quando viene usata come forma di polimorfismo...
POLIMORFISMO
E veniamo all'ultima, non per importanza, delle tre caratteristiche fondamentali della OOP, il polimorfismo, ovvero, come ci sugegrisce il nome stesso, la capacità di un oggetto di assumere più forme. Penso che il modo migliore di spiegare questo concetto sia attraverso un esempio. Riprendiamo le classi Utente, Cliente e Fornitore e supponiamo che in Utente esista un metodo che ritorni la propria denominazione:

Consideriamo il seguente listato:

quale sarà l'output prodotto? Lanciamo...ed ecco il risultato:

Sorpresi? Spero di no: Nonostante siano tutti definiti come utenti, la classe concreta di ogni istanza è diversa.
Facciamo un passo in più...ridefiniamo il metodo ChiSono() nella classe Cliente e Fornitore:


Rilanciano il listato precedente, il risultato questa volta è:

Questa volta il meccanismo del polimorfismo invoca il metodo della classe concreta anzichè quella del tipo della variabile fornendo il risultato illustrato. Come potete notare è necessario marcare i metodi come sovrascrivibili (Overridable in Visual Basic, virtual in C#) per poter ottenere questo effetto. Questo tipo di polimorfimo ha un nome è si chiama OVERRIDE, da non confondere con una seconda forma di polimorfismo molto interessante che si chiama OVERLOAD che permette di avere all'interno della stessa classe o in classi della stessa catena di ereditarietà metodi con lo stesso nome ma con numero e/o tipo di parametri diversi:


Il listato:


comporta il seguente risultato:

Molto probabilmente non riuscirete subito a cogliere le potenzialità di questo meccanismo e non mi illudo di riuscire a farlo: vedremo nel corso di questi articoli come questa caratteristica della OOP abbia reso possibile la realizzazione di molti meccanismi dei moderni framework di sviluppo.
Il contributo di Microsoft alla OOP
A questi meccanismi ormai consolidati e supportati da tutti i linguaggi ad oggetti, Microsoft ha aggiunto due nuovi concetti: le PROPERTIES e le CLASSI PARTIAL.
Volendo mantenere l'incapsulamento ma permettere ad una classe di esporre i proprio attributi in maniere "sicura" potendone controllare l'accesso, basta creare due metodi, spesso chiamati getter e setter, pubblici all'interno dei quali gli attributi possono essere restituiti o impostati:


Dato che questa esigenza è molto comune nello sviluppo software la Microsoft ha pensato di introdurre il concetto di property: un ibrido tra attributo e metodo che rendesse palese ed elegante l'implementazione dei getter e dei setter:


L'accesso a questi metodi avviene come se fossero degli attributi pubblici:


Le classi partial invece sono un sistema che permette di definire una classe in più file. Grazie a questo meccanismo, che si implementa semplicemente anteponendo la parola chiave Partial in Visual Basic, partial in C#, al nome della classe nella sua definizione, viene risolto un problema molto sentito dai programmatori che si avvalgono di strumenti di sviluppo RAD come Visual Studio in cui alcune parti delcodice vengono generate dal tool: le eventuali aggiunte al codice generato possono essere fatte in un file separato, in questo modo eventuali aggirnamenti del codice da parte del tool a seguito di modifiche non sovrascriveranno le aggiunte fatte dal programmatore che si trovano in un file separato.
Conclusioni
Penso che per oggi possa bastare, abbiamo messo molta carne a cuocere, appuntamento al prossimo articolo in cui parleremo di interfacce, classi astratte e classi generiche.
Happy Coding.