Object Oriented Programming

28

Transcript of Object Oriented Programming

Introduzione alla programmazione orientata agli oggetti

Di Massimiliano Brolli

Premessa ................................................................................................... 3

Prerequisiti ................................................................................................ 3

La Storia.................................................................................................... 5

Grandi vantaggi .......................................................................................... 8

I concetti base ........................................................................................... 9

Le Classi ............................................................................................... 10

Gli attributi o variabili di classe ............................................................. 12

I metodi ............................................................................................. 13

Il concetto di Oggetto ............................................................................. 14

Incapsulamento ..................................................................................... 16

L’Ereditarietà ......................................................................................... 18

Il Polimorfismo ...................................................................................... 20

Le interfacce ....................................................................................... 20

Le classi astratte ................................................................................. 24

L’overriding ........................................................................................ 26

L’overloads ......................................................................................... 28

Premessa La programmazione orientata agli oggetti (OOP, Object Oriented Programming) è un paradigma di programmazione, che prevede la definizione di oggetti software che interagiscono gli uni con gli altri attraverso lo scambio di messaggi. Un linguaggio di programmazione è definito ad oggetti quando permette di implementare i seguenti tre meccanismi:

1. Incapsulamento : prevede di raggruppare in un'unica entità (la classe) la definizione delle strutture dati e delle procedure che operano su di esse. Le classi definiscono per l'appunto degli "oggetti" software dotati di attributi (variabili di classe, proprietà e strutture) e metodi (procedure) che operano sui dati dell'oggetto stesso.

2. Ereditarietà : permette essenzialmente di definire delle classi a partire da altre già definite.

3. Polimorfismo : Il Polimorfismo permette al programmatore di creare operazioni diverse con lo stesso nome che semplifica complessi modelli di classe, e lo rende più comprensibile e gestibile.

La programmazione orientata agli oggetti può essere anche vista come un supporto alla modellazione software degli oggetti del mondo reale o del modello astratto. Inoltre, l'organizzazione del codice sotto forma di classi fornisce un supporto più naturale al riuso del codice.

Prerequisiti Per procedere alla lettura di questo documento occorre aver letto le seguenti dispense :

• Introduzione agli algoritmi. • La scelta tra Microsoft.NET & Java • Predisposizione Virtual Machine di base • Predisposizione Virtual Machine per l'ambiente di sviluppo Java • Introduzione alla scrittura del codice JAVA • Scrivere applicazioni JAVA con Eclipse

Oppure

• Introduzione agli algoritmi. • La scelta tra Microsoft.NET & Java • Predisposizione Virtual Machine di base • Predisposizione Virtual Machine per l'ambiente di sviluppo Microsoft .NET • Introduzione alla scrittura del codice .NET • Scrivere applicazioni .NET con Visual Studio

Nota Bene : Tale dispensa non si lega ad un determinato linguaggio di programmazione, gli esempi riportati sono scritti in diversi linguaggio proprio per dimostrare che il concetto di Object Oriented non è legato ad una sola implementazione.

La Storia Il termine Object (Oggetto) e il termine Oriented (Orientato verso qualcosa) nel senso moderno della programmazione vede la luce al MIT (Massachusetts Institute of Technology) tra il 1950 e il 1960. Il primo linguaggio di programmazione orientato agli oggetti fu il Simula 67 sviluppato nel 1967 in seguito ad una profonda rivisitazione del linguaggio Simula I. Simula introduce il concetto di classi e di istanza di oggetto così come le sottoclassi, i metodi così come oggi le conosciamo e influenzò molto i successivi linguaggi di programmazione orientati agli oggetti tra i quali Smalltalk e C++. Dopo Simula 67 venne sviluppato negli anni settanta Smalltalk (dalla Xerox) inizialmente in sordina vide il suo successo dopo la pubblicazione nell’agosto del 1981 di un lungo articolo sulla rivista Byte Magazine. Di seguito una schermata di Smalltalk.

Proprio in quel periodo i Mainframe dominavano il mondo e si cominciava a passare dalle schede perforate ai linguaggi di programmazione strutturati come ad esempio il Cobol in maniera lenta e graduale. Di seguito una immagine di un Datacenter con supporti a nastro e una schermata

del TSO, ambiente di sviluppo per Cobol/Cobol2.

Verso la fine degli anni 70 Lisp comincia a farsi notare, linguaggio che prende a vantaggio quanto di buono fatto su Smalltalk e ne estende le funzionalità come ad esempio l’introduzione dell’ereditarietà multipla tanto da arrivare al Common Lisp Object System e allo standard ANSI Common Lisp. Ci furono anche dei tentativi di produrre Hardware con capacità orientate alla compilazione di programmi OO quali APX Intel 432 e la Linn intelligente Rekursiv che però fallirono miseramente. Molti anni dovranno passare prima di utilizzare in maniera industriale un linguaggio orientato agli oggetti anche tenendo in considerazione le alte capacità computazionali richieste alle CPU dell’epoca per applicare le rigide regole OO. Negli anni ottanta sono state create estensioni orientate ad oggetti del linguaggio C (C++, Objective C, e altri), e di altri linguaggi (Object Pascal) fino ad arrivare agli anni 90 dove divenne il paradigma dominante di tutti i linguaggi di programmazione come C++, Java, Delphi, Python, C#, Microsoft .NET, Perl, PHP (a partire dalla versione 5). Essa rappresenta una delle idee più interessanti introdotte ultimamente nel campo della programmazione, in grado di risolvere i problemi di complessità e ingovernabilità che si presentavano nei progetti di grandi dimensioni. A differenza della programmazione “control-flow” o programmazione strutturata, (per intenderci, quella classica di C, Pascal, Cobol), in cui un programma viene inteso come una sequenza di azioni, nell’OOP un programma è considerato come un insieme di oggetti o di programmi che lavorano assieme in un modo prestabilito allo scopo di perseguire un determinato obiettivo. Un esempio molto bello sono i mattoncini della Lego.

In effetti per costruire un castello ne occorrono moltissimi, di tanti colori e di diversa forma e tutti quanti saranno importanti in egual modo e avranno un compito prestabilito.

Un programma di grandi dimensione è come un castello Lego, composto da una moltitudine di classi le quali collaborano tra di loro scambiandosi messaggi e risolvendo i nostri algoritmi. Quindi una classe possiamo definirla come un piccolo programma. E’ un elemento autonomo, dotato di proprie qualità e di un proprio comportamento preciso che può essere richiamato da una qualsiasi altra classe. Ovviamente tutte queste classi per interfacciarsi dovranno per forza di cose sapere in che maniera dialogare tra loro per raggiungere tutti insieme un determinato obbiettivo.

Grandi vantaggi Molti sono i vantaggi che si possono avere passando dalla programmazione strutturata ad una programmazione Object oriented. Però per ottenere il massimo dall’OOP occorre progettare in maniera adeguata gli oggetti che si vogliono utilizzare, avendo molta cura nel rispettare le poche ma importanti regole che vedremo nel dettaglio più in avanti. Di fatto i vantaggi sono :

• Forte manutenibilità : Le modifiche sui dati sono normalmente limitate all'ambito omogeneo della classe, poiché i dati sono accessibili solo alle operazioni interne alle classi

• Riusabile : visto che la modularità è elevata, grazie all’ereditarietà e al polimorfismo (che vedremo più avanti) è possibile riutilizzare parti di codice precedentemente scritte o intere classi in altri contesti.

In effetti esistono anche degli svantaggi i quali se non conosciuti portano a vanificare completamente i vantaggi che abbiamo appreso sopra e sono :

• Progettazione accurata : Un progetto OOP richiede maggiore attenzione nella progettazione. Strutturare le classi in modo fortemente manutenibile è un lavoro di analisi molto complesso che però è compito di chi progetta il software e non di chi lo sviluppa.

Inoltre spesso si incontrano programmatori che si complicano la vita perché non sono a conoscenza delle basi della programmazione orientata agli oggetti. Chi di voi suona sa che il solfeggio, l’armonia, il contrappunto sono la base della musica. Queste regole sono valide sia se si suona il pianoforte e sia se si suona uno strumento a fiato. Tutto questo per dirvi che l’OOP è la base unica di tutti i linguaggi di programmazione. Conoscendo approfonditamente l’OOP si può passare da Java a .NET o da .NET a C++ in maniera molto indolore in quanto tutti questi ambienti si basano sulla stessa metodologia. Conoscere l’Object Oriented è la base per divenire buoni programmatori Java o .NET.

I concetti base Di seguito andremo a dettagliare i concetti base della programmazione Object Oriented. Questo documento non mostrerà come l’OOP viene implementato su un dato linguaggio di programmazione ma si limiterà a descrivere il funzionamento in maniera astratta dalla sua reale implementazione. Infatti tali concetti esulano completamente dalla tecnologia adoperata, mentre è la tecnologia stessa che deve essere conforme alla programmazione ad oggetti.

Le Classi

Iniziamo questo paragrafo definendo che una classe è un piccolo programma. Di fatto l’insieme di più classi costituiscono l’insieme elaborativo del nostro programma come precedentemente abbiamo visto per i mattoncini Lego. La classe è composta da Strutture dati e codice che le gestisce.

Un esempio di una classe Java public class Impiegato {

private String nome;

private String cognome;

private int annoNascita;

private int stipendio;

private String posizione;

final int stipendioMax;

final int stipendioMin;

public Impiegato (String nome, String cognome,

int annoNascita, String posizione)

{

stipendioMin = 750;

stipendioMax = 2000;

this.nome = nome;

this.cognome = cognome;

this.annoNascita = annoNascita;

stipendio = stipendioMin;

this.posizione = posizione;

}

public void mostraNome(){

System.out.println(nome + " " + cognome);

}

public int calcolaStipendio (){

return stipendio;

}

public void aumentaStipendio (int incremento){

variaStipendio (incremento);

}

public void riduciStipendio (int riduzione){

variaStipendio (riduzione*(-1));

}

private void variaStipendio (int variazione){

if (stipendio + variazione > stipendioMin)

stipendio = stipendio + variazione;

else

stipendio = stipendioMin;

if (stipendio > stipendioMax)

stipendio = stipendioMax;

}

}

Un esempio di una classe VB.NET. Per farlo funzionare è possibile creare una Console Application nella quale incollare il Module1 e la classe Impiegato. 'Modulo della console application che lancierà la classe Impiegato

Module Module1

Sub Main()

Dim impiegato As New Impiegato("Massimiliano", "Brolli", "1973", "Quadro")

impiegato.StampaValori()

System.Console.WriteLine("Età : " & impiegato.CalcolaEta(2011))

Threading.Thread.Sleep(10000)

End Sub

End Module

'Classe impiegato da creare tramite Add->New Item->Class

Public Class Impiegato

Private nome As String

Private cognome As String

Private annoNascita As Integer

Private stipendio As Integer

Private posizione As String

Private stipendioMax As Integer = 2000

Private stipendioMin As Integer = 1000

Public Sub New(ByVal nome As String,

ByVal cognome As String,

ByVal annoNascita As Integer,

ByVal posizione As String)

stipendioMin = 750

stipendioMax = 2000

Me.nome = nome

Me.cognome = cognome

Me.annoNascita = annoNascita

stipendio = stipendioMin

Me.posizione = posizione

End Sub

Private Sub VariaStipendio(ByVal Variazione As Integer)

If (stipendio + Variazione > stipendioMin) Then

stipendio = stipendio + Variazione

Else

stipendio = stipendioMin

End If

If (stipendio > stipendioMax) Then

stipendio = stipendioMax

End If

End Sub

Public Sub StampaValori()

System.Console.WriteLine("Nome inserito : " & nome)

System.Console.WriteLine("Cognome inserito : " & cognome)

System.Console.WriteLine("Anno di nascita : " & annoNascita)

System.Console.WriteLine("Stipendio : " & stipendio)

System.Console.WriteLine("Posizione : " & posizione)

System.Console.WriteLine("Stipendio Massimo : " & stipendioMax)

System.Console.WriteLine("Stipendio Minimo : " & stipendioMin)

End Sub

Public Sub MostraNome()

System.Console.Write(nome + " " + cognome)

End Sub

Public Function CalcolaStipendio() As Integer

Return stipendio

End Function

Public Function CalcolaEta(ByVal annoCorrente As Integer) As Integer

Dim eta As Integer = annoCorrente - annoNascita

If ((eta >= 16) And (eta <= 90)) Then

Return eta

Else

Return 0

End If

End Function

Public Sub AumentaStipendio(ByVal incremento As Integer)

VariaStipendio(incremento)

End Sub

Public Sub RiduciStipendio(ByVal riduzione As Integer)

VariaStipendio(riduzione * (-1))

End Sub

End Class

I membri di una classe infatti sono:

• Gli attributi : le strutture dati che la costituiscono come variabili di classi, proprietà, strutture.

• I Metodi : procedure o codice che ne descrive il comportamento.

Gli attributi o variabili di classe

Gli attributi (o variabili di classe) descrivono le strutture dei dati di cui una classe è composta. Di fatto sono delle variabili visibili da tutti i metodi e proprietà della classe stessa nelle quali verranno memorizzate informazioni utili al suo ciclo di vita. In C# public class Famiglia {

public String nome;

static String cognome;

public int annoNascita;

}

In VB.NET Public Class Employee

Private _name As String

Protected _surname As String

Shared _group As String

End Class

In Java package utilityClass;

public class Employee{

private String name;

private String surname;

static String group;

}

Ogni linguaggio di programmazione implementa delle direttive di inizializzazione dello scope delle variabili. Per scope si intende la visibilità delle variabili o meglio dire come queste saranno visibili dalle loro istanze. Tralasciando il concetto di private, protected/protect, public che avremo già visto nelle lezioni specializzate all’apprensione della sintassi dei linguaggi di programmazione, mi vorrei soffermare sull’uso della direttiva static in Java o Shared in .NET.

La direttiva Shared permette di marchiare una variabile di classe in modo condiviso. Ad esempio in una classe Famiglia sarà presente la variabile pubblica nome, la variabile pubblica annoDiNascita e la variabile static/Shared cognome. Per static (in Java) o Shared (in VB.NET) si intende che la variabile cognome verrà allocata una sola volta per tutte le istanze della classe Famiglia e quindi risulterà condivisa. Infatti mentre per ogni istanza potremo modificare il valore di nome e annoDiNascita, modificando la variabile cognome tale modifica sarà vista su tutte le istanze che ne fanno uso.

I metodi

Come abbiamo detto in precedenza, i metodi racchiudono il funzionamento specifico di una data classe e al loro interno come visto nell’esempio precedente è presente il codice applicativo. La prima riga di un metodo viene definita Firma public void aumentaStipendio (int incremento){

variaStipendio (incremento);

}

La firma o segnatura (dall'inglese signature) di un metodo è costituita da un insieme di informazioni che ne identificano univocamente il metodo fra quelli della sua classe di appartenenza. Tali informazioni includono generalmente il nome del metodo, il numero e il tipo dei suoi parametri e il tipo del suo valore restituito, sebbene nella terminologia tecnica dei diversi linguaggi l'espressione firma assuma talvolta un significato più specifico, includendo informazioni aggiuntive o non includendo alcune di quelle citate (per esempio il tipo del valore restituito oppure i parametri di ingresso). Come per gli attributi, è possibile marchiare con static/Shared i metodi delle classi. Questo permetterà di utilizzare tali metodi senza andare ad istanziare tale classe. Ovviamente però se in una data classe è presente un metodo di tipo static/Shared, tutti gli altri dovranno essere esposti nella stessa maniera.

Il concetto di Oggetto

Un oggetto è una istanza di una classe. Oggetto o istanza in Object Oriented sono sinonimi. Una istanza è la realizzazione della classe nella memoria dell’elaboratore, daquel momento dopo che tale classe sia stata istanziata sarà eseguibile. La definizione di una classe è presente all’interno di un file di testo nel quale vengono definiti come i dati saranno organizzati in memoria e sono una serie di istruzioni a noi comprensibili che però non lo sono per un elaboratore che riconosce ed elabora solo istruzioni in codice macchina. Per rendere eseguibile una classe occorre creare una istanza che possa essere eseguita. Una istanza occupa memoria, la classe no, la occupa solo sul file System. Per creare una istanza occorre specificare la clausola new. Dim sw As New StreamWriter("c:\test.txt")

Dim sw As StreamWriter

sw = New StreamWriter("c:\test.txt")

Nell’esempio precedente in VB.NET viene dichiarata l’istanza sw e tramite la clausola New viene allocata in memoria. Eseguita tale istruzione l’istanza sw sarà disponibile, pertanto sarà utilizzabile dal nostro programma. Ogni oggetto possiede tutti gli attributi definiti nella classe, ed essi hanno un valore, che può mutare durante l'esecuzione del programma come quello di qualsiasi variabile. Di seguito vengono mostrati gli attributi di una classe, tali attributi hanno una loro visibilità all’interno e all’esterno della classe e devono essere accessibili solo attraverso dei metodi. Una classe in Java public class Impiegato {

private String nome;

private String cognome;

private int annoNascita;

private int stipendio;

private String posizione;

final int stipendioMax;

final int stipendioMin;

} Molti linguaggi forniscono un supporto per l'inizializzazione automatica di un oggetto, con uno o più speciali metodi detti costruttori. Inizialmente si fa fatica a capire queste sottili differenze e spesso si confondono i termini. Sicuramente il tempo ne faciliterà l’uso oltre allo studio intensivo e la pratica su questi argomenti.

Incapsulamento

L'incapsulamento (o information hiding) permette di limitare l’utilizzo di una classe solo per quelle parti di codice strettamente necessarie che permetteranno la risoluzione dell’algoritmo. Molti metodi e variabili sono d’importanza locale per la classe e non dovranno essere visibili e modificabili dall’utilizzatore pena il corretto funzionamento di essa se modificati e pena la corretta comprensione della classe. Questo meccanismo viene permesso utilizzando la classe attraverso la sua interfaccia o modificare le variabili locali solo tramite dalle proprietà di classe e utilizzando in maniera sapiente lo scope delle variabili. Gestito in maniera intelligente, l'incapsulamento permette di vedere l'oggetto come una black-box, cioè una scatola nera di cui, attraverso ad esempio una interfaccia, è noto cosa fa e come interagisce con l'esterno ma non come lo fa. I vantaggi principali portati dall'incapsulamento sono :

• Robustezza • Indipendenza • Riusabilità • Facilità di utilizzo delle classi

Di seguito un esempio in Java package useIncapsulation;

public interface Report {

public abstract void Inizialize(String Title, String Text);

public abstract void Generate();

}

package useIncapsulation;

public class ReportVerticalConsole

implements Report{

private String[] Array;

private String Title;

private String Text;

@Override

public void Generate() {

BuildVerticalArray();

for(int i=0; i<Array.length; i++)

System.out.println(Array[i]);

}

@Override

public void Inizialize(String Title, String Text) {

this.Title = Title;

this.Text = Text;

}

private void BuildVerticalArray(){

Array = Text.split("\\,");

}

}

package useIncapsulation;

public class Incapsulation {

public static void main(String[] args) {

Report report = new ReportVerticalConsole();

report.Inizialize("Report numero uno", "12,23,43,23,54,64,44");

report.Generate();

}

}

Da notare che nel metodo Main verrà creato l’ogetto di tipo ReportVerticalConsole di tipo Report, interfaccia di ReportVerticalConsole dove in tale interfaccia saranno definiti solo i metodi Inizialize e Generate. Tutto il resto del funzionamento di ReportVerticalConsole sarà incapsulato al suo interno e quindi conosciuto (il concetto della black Box) come ad esempio il funzionamento della Routine BuildVerticalArray().

L’Ereditarietà

L'OOP prevede un meccanismo molto importante chiamato l'ereditarietà. L’ereditarietà permette di estendere nuove classi a partire da classi già definite. Permette inoltre di aggiungere nuovi membri ad una classe, e di modificare il comportamento dei metodi, in modo da adattarli alla nuova esigenza senza dover riscrivere il codice della classe padre.

• Da una stessa classe è possibile costruire diverse classi derivate. • Da una classe derivata è possibile derivarne una ulteriore con lo stesso

meccanismo. La classe derivata, o sottoclasse, eredita tutti i metodi e gli attributi della classe "genitrice", e può aggiungere nuovi membri alla classe, sia attributi che metodi, e/o ridefinire il codice di alcuni metodi attraverso l’ausilio del Polimorfismo che vedremo nel capitolo successivo. Di seguito un semplice esempio scritto in VB.NET 'Modulo Main della Console Application

Module Module1

Sub Main()

Dim annoCorrente As New AnnoCorrente("2011", "10", "11")

annoCorrente.PrintValue()

System.Console.WriteLine("-------------------------------------------------")

Dim annoCorrenteComplesso As New AnnoCorrenteComplesso("2011", "10", "11")

annoCorrenteComplesso.PrintValue()

Threading.Thread.Sleep(10000)

End Sub

End Module

'Classe AnnoCorrente

Public Class AnnoCorrente

Private _anno As String

Private _mese As String

Private _giorno As String

Public ReadOnly Property anno() As String

Get

Return _anno

End Get

End Property

Public ReadOnly Property mese() As String

Get

Return _mese

End Get

End Property

Public ReadOnly Property giorno() As String

Get

Return _giorno

End Get

End Property

Public Sub New(ByVal anno As String, _

ByVal mese As String, _

ByVal giorno As String)

_anno = anno

_mese = mese

_giorno = giorno

End Sub

Public Sub PrintValue()

System.Console.WriteLine(_giorno & "/" & _mese & "/" & _anno)

End Sub

End Class

'Classe AnnoCorrenteComplesso che eredita dalla classe AnnoCorrente

Public Class AnnoCorrenteComplesso

Inherits AnnoCorrente

Public Sub New(ByVal anno As String, _

ByVal mese As String, _

ByVal giorno As String)

'sono costretto da invocare il costruttore della classe ereditata

MyBase.new(anno, mese, giorno)

End Sub

Public Function CurrentDate() As String

Return System.DateTime.Now.Date

End Function

Public Sub PrintValue()

System.Console.WriteLine("Giorno : " & MyBase.giorno)

System.Console.WriteLine("Mese : " & MyBase.mese)

System.Console.WriteLine("Anno : " & MyBase.anno)

End Sub

End Class

E un semplice esempio scritto in Java

package example;

public class ExtendsClass {

private int valueA;

private int valueB;

public int getValueA() {

return valueA;

}

public int getValueB() {

return valueB;

}

public ExtendsClass(int valueA, int valueB){

this.valueA = valueA;

this.valueB = valueB;

}

}

package example;

public class UseExtendsClass extends ExtendsClass{

public UseExtendsClass(int valueA, int valueB) {

//sono costretto ad invocare il costruttore della classe ereditata

super(valueA, valueB);

}

}

Noteremo in entrambi gli esempi che se la classe padre ha un costruttore il quale deve essere eseguito saremo costretti nella classe figlia a dover definire in essa un costruttore e di conseguenza lanciare il costruttore della classe padre passandogli gli argomenti necessarie per il suo instanziamento.

Il Polimorfismo

Per polimorfismo (capacità di essere diversi) si intende quella possibilità che le classi hanno di implementare in modo differente i metodi e le proprietà che le descrivono. Il vantaggio principale del polimorfismo è che si può fare uso di oggetti che espongono una stessa interfaccia, ma implementazioni assolutamente diverse. L'interfaccia definisce un contratto generale che sottoclassi diverse possono soddisfare in modi diversi ma tutte conformi alla specifica comune stabilita dall’interfaccia stessa (metodi e proprietà definiti in essa). In virtù di questa possibilità, si può utilizzare le stesse firme esposte dall’interfaccia personalizzandone o modificandone anche radicalmente il comportamento. Il polimorfismo viene permesso grazie ai concetti di Interfaccia, Overloading dei metodi o l’Overriding delle classi.

Le interfacce

L’interfaccia è una classe che definisce le strutture dati e i metodi da implementare ma non ne descrive il comportamento (il codice). Di fatto una interfaccia serve quando differenti classi devono essere standardizzate o meglio dire disporre di funzionalità comuni. L’interfaccia non è istanziabile come una comune classe e quindi non può essere caricata in memoria ma fornisce direttive alle classi che la utilizzano su cosa dovranno implementare al loro interno. Le interfacce spesso rappresentano il componente di livello più alto di un'applicazione. Esse possono essere usate per migliorare l'estensibilità e la riusabilità di un programma object oriented. Nell’esempio sotto riportato scritto in VB.NET viene creata l’interfaccia IVeicle nella quale sono presenti 3 proprietà e un metodo. La classe Car che implementa l’interfaccia IVeicle è costretta ad implementare al suo interno quanto descritto dall’interfaccia.

'Main della Console Application

Module Module1

Sub Main()

Dim car As New Car(2000, 120, 130)

car.GetInfo()

Dim airplane As New Airplane(2000, 120, 130, 110)

airplane.GetInfo()

Threading.Thread.Sleep(10000)

End Sub

End Module

'Interfaccia IVeicle dalla quale implementeranno le successive due classi

Public Interface IVeicle

ReadOnly Property GetVelocity As Integer

ReadOnly Property GetWeight As Integer

ReadOnly Property GetDisplacement As Integer

Sub GetInfo()

End Interface

'Classe Car che implementa IVeicle

Public Class Car

Implements IVeicle

Private Displacement As Integer

Private Velocity As Integer

Private Weight As Integer

Public Sub New(ByVal Displacement As Integer, _

ByVal Velocity As Integer, _

ByVal Weight As Integer)

Me.Displacement = Displacement

Me.Weight = Weight

Me.Velocity = Velocity

End Sub

Public ReadOnly Property GetDisplacement As Integer Implements IVeicle.GetDisplacement

Get

Return Displacement

End Get

End Property

Public ReadOnly Property GetVelocity As Integer Implements IVeicle.GetVelocity

Get

Return velocity

End Get

End Property

Public ReadOnly Property GetWeight As Integer Implements IVeicle.GetWeight

Get

Return Weight

End Get

End Property

Public Sub GetInfo() Implements IVeicle.GetInfo

System.Console.Write("La tipologia del veicolo è : [Automobile]" & vbCrLf)

System.Console.Write("La velocità del veicolo è : " & GetVelocity & vbCrLf)

System.Console.Write("Il peso del veicolo è : " & GetWeight & vbCrLf)

System.Console.Write("La cilindrata del veicolo è : " & GetDisplacement & vbCrLf)

End Sub

End Class

'Classe Airplane che implementa IVeicle

Public Class Airplane

Implements IVeicle

Private Displacement As Integer

Private Velocity As Integer

Private Weight As Integer

Private wingspan As Integer

Public Sub New(ByVal Displacement As Integer, _

ByVal Velocity As Integer, _

ByVal Weight As Integer, _

ByVal wingspan As Integer)

Me.Displacement = Displacement

Me.Weight = Weight

Me.Velocity = Velocity

Me.wingspan = wingspan

End Sub

Public ReadOnly Property GetDisplacement As Integer Implements IVeicle.GetDisplacement

Get

Return Displacement

End Get

End Property

Public ReadOnly Property GetWingspan As Integer

Get

Return wingspan

End Get

End Property

Public ReadOnly Property GetVelocity As Integer Implements IVeicle.GetVelocity

Get

Return Velocity

End Get

End Property

Public ReadOnly Property GetWeight As Integer Implements IVeicle.GetWeight

Get

Return Weight

End Get

End Property

Public Sub GetInfo() Implements IVeicle.GetInfo

System.Console.Write("La tipologia del veicolo è : [Areoplano]" & vbCrLf)

System.Console.Write("La velocità del veicolo è : " & GetVelocity & vbCrLf)

System.Console.Write("Il peso del veicolo è : " & GetWeight & vbCrLf)

System.Console.Write("La cilindrata del veicolo è : " & GetDisplacement & vbCrLf)

System.Console.Write("L'apertura alare del veicolo è : " & GetWingspan & vbCrLf)

End Sub

End Class

Ciò non toglie che se volessimo creare la classe Elicopter anche questa potrà implementare l’interfaccia IVeicle. Infatti il concetto astratto di Veicolo e univoco e quindi i metodi presenti all’interno dell’interfaccia IVeicle potranno descrivere e accomunare le due classi Elicottero e Car e Airplane. Ora un esempio in Java package interfaceCars;

public interface ICars {

public void Body();

public void Velocity(String value);

public void Fuel(int value);

}

Il perché utilizzare le interfacce diventa chiaro quando abbiamo classi diverse che però svolgono simili funzioni. Pensiamo ad esempio ai driver che permettono alle nostre applicazioni di connettersi ai database. Ogni driver(sviluppato da persone differenti) permette di eseguire le stesse

funzioni, ma su database diversi (es. SQL Server ed Oracle). Se ognuna di queste due classi implementasse la connessione al DB con metodi differenti, ad esempio la prima con il metodo Open e l’altra con il metodo Connection, il programmatore sarebbe costretto ad imparare il funzionamento di entrambe. Creando l’interfaccia System.Data.IDbConnection standardizzeremo le strutture dati principali. Ora se tutti i driver dei diversi produttori implementassero tale interfaccia sarebbero costretti a scrivere i metodi Open, Close, BeginTrasaction, ecc… Di conseguenza tutti i driver disporranno di strutture dati omogenee ma con codice al loro interno differente e quindi il programmatore conosciuto il funzionamento dell’interfaccia, potrà tranquillamente trascurare il comportamento di ogni singola classe. Per concludere, esistono alcuni linguaggi di programmazione che permettono l’uso di interfacce multiple come ad esempio Java. Di fatto questa funzionalità non è molto utilizzata ma può essere utili in speciali condizioni dove l’uso delle interfacce si fa molto intensivo.

Le classi astratte

La classe astratta è una interfaccia che oltre a fornire firme sulle strutture dati da implementare contiene al suo interno del proprio codice. Nell’esempio successivo scritto in C# abbiamo la classe astratta ClassAbstract nella quale viene definito il metodo Add e viene implementato il metodo ReturnValue. A differenza dell’interfaccia (dato che la classe astratta al suo interno è presente del codice) deve essere ereditata e non implementata. using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace ConsoleApplication1

{

class Program

{

static void Main(string[] args)

{

ClassUsingAbstract a = new ClassUsingAbstract();

a.Add(12, 23);

System.Console.Write(a.GetResult());

}

}

public abstract class ClassAbstract

{

public abstract void Add(int a, int b);

public string ReturnValue(int value)

{

return "Il valore è : " + value;

}

}

class ClassUsingAbstract : ClassAbstract

{

int sum;

public override void Add(int a, int b)

{

sum = a + b;

}

public string GetResult()

{

return ReturnValue(sum);

}

}

}

E un esempio in VB.NET

Module Module1

Sub Main()

Dim dog As New Dog

dog.print("Il cane")

Dim fish As New Fish

fish.print("Il pesce")

Threading.Thread.Sleep(10000)

End Sub

End Module

'Classe astratta Animals

Public MustInherit Class Animals

Public MustOverride Function Mangia() As String

Public MustOverride Function Vive() As String

Public Sub print(ByVal TipoAnimale As String)

System.Console.WriteLine(TipoAnimale & " Mangia : " & Mangia())

System.Console.WriteLine(TipoAnimale & " Vive : " & Vive())

End Sub

End Class

Public Class Dog

Inherits Animals

Public Overrides Function Mangia() As String

Return "La carne"

End Function

Public Overrides Function Vive() As String

Return "sulla terra"

End Function

End Class

Public Class Fish

Inherits Animals

Public Overrides Function Mangia() As String

Return "Il plancton"

End Function

Public Overrides Function Vive() As String

Return "nel mare"

End Function

End Class

Un esempio in Java package animals;

public abstract class Animals {

public abstract String Cammina();

public abstract String Mangia();

public abstract String Vive();

}

La classe Astratta è meno autoritativa rispetto all’interfaccia che descrive solo le strutture dati. Le classi astratte vengono in forte aiuto quando occorre definire degli standard ma specializzarne anche un comportamento. E’ molto importante capire la differenza tra classe astratta e Interfaccia e spesso (in particolare agli inizi) occorre chiedersi se sia più giusto utilizzare una o l’altra.

L’overriding

Per Overriding (sovrascrittura in italiano) si intende la capacità di ridefinire un metodo presente nella classe ereditata. il metodo originale (presente nella classe padre) e quello che lo ridefinisce (presente nella classe figlia) devono avere necessariamente la stessa firma e quindi lo stesso nome, lo stesso tipo di ritorno e la stessa lista di parametri di ingresso, di fatto la firma deve essere identica. Sarà il compilatore in fase di run-time a determinare quali dei due dovrà essere eseguito. Nell’esempio sotto riportato scritto in VB.NET, la classe Cars e Airplane ereditano entrambe dalla classe Veicle la quale mette a loro disposizione una serie di proprietà comuni Public Class Veicle

Private Velocity As Integer

Private Weight As Integer

Private Displacement As Integer

Public Sub New(ByVal Velocity As Integer, _

ByVal Weight As Integer, _

ByVal Displacement As Integer)

Me.Velocity = Velocity

Me.Weight = Weight

Me.Displacement = Displacement

End Sub

Public ReadOnly Property getVelocity() As Integer

Get

Return Velocity

End Get

End Property

Public ReadOnly Property getWeight() As Integer

Get

Return Weight

End Get

End Property

Public ReadOnly Property getDisplacement() As Integer

Get

Return Displacement

End Get

End Property

Public Overridable Sub GetInfo()

System.Console.Write("La velocità del veicolo è : " & getVelocity & vbCrLf)

System.Console.Write("Il peso del veicolo è : " & getWeight & vbCrLf)

System.Console.Write("La cilindrata del veicolo è : " & getDisplacement & vbCrLf)

End Sub

End Class

Public Class Cars

Inherits Veicle

Public Sub New()

MyBase.new(160, 1300, 1200)

End Sub

End Class

Public Class Airplane

Inherits Veicle

'Definisco in più l'ampiezza alare in quanto propria della classe Aeroplano

Private Wingspan As Integer

Public ReadOnly Property getWingspan() As Integer

Get

Return Wingspan

End Get

End Property

Public Sub New(ByVal Wingspan As Integer)

MyBase.New(350, 4000, 8000)

Me.Wingspan = Wingspan

End Sub

'Ridefinisco tramite il polimorfismo il metodo GetInfo

Public Overrides Sub GetInfo()

System.Console.Write("L'aperura alare è : " & getWingspan & vbCrLf)

System.Console.Write("La velocità del veicolo è : " & getVelocity & vbCrLf)

System.Console.Write("Il peso del veicolo è : " & getWeight & vbCrLf)

System.Console.Write("La cilindrata del veicolo è : " & getDisplacement & vbCrLf)

End Sub

End Class

‘Modulo per la creazione degli oggetti delle classi su Console Application .NET

Module Module1

Sub Main()

Dim A1 As New Cars()

A1.GetInfo()

Dim A2 As New Airplane(64)

A2.GetInfo()

End Sub

End Module

Nel caso però della classe Airplane, occorre definire oltre che alle proprietà comuni implementate in Veicle la proprietà Wingspan la quale viene inserita all’interno di Airplane. Inoltre, visto che il metodo GetInfo di Veicle non riporta anche la visualizzazione della proprietà Wingspan, il metodo viene sovrascritto nella classe Airplane ma rimane immutato il suo funzionamento all’interno della classe padre Veicle.

L’overloads

Per Overloads (sovraccarico in italiano) si intende la capacità di poter descrivere metodi con lo stesso nome ma con firma differente all’interno della stessa classe. Un esempio di Overload viene riportato di seguito con il linguaggio C#. Noteremo che l’unica cosa che viene mantenuta è il nome del metodo, ma i parametri di input devono per forza essere differenti da metodo a metodo. Un esempio in C# class ExampleOverload

{

public void sum(int a, int b) {

}

public int sum(int a, int b, int c){

return a + b + c;

}

public String sum(int a, int c){

return System.Convert.ToString(a + c);

}

}

Un esempio in VB.NET Public Class OverloadClass

Public Sub PrintValue(ByVal Nome As String)

System.Console.WriteLine("Nome : " & Nome)

End Sub

Public Sub PrintValue(ByVal Nome As String, ByVal Cognome As String)

System.Console.WriteLine("Nome : " & Nome)

System.Console.WriteLine("Cognome : " & Cognome)

End Sub

Public Function PrintValue() As String

Return "Massimiliano Brolli"

End Function

End Class

Non è possibile avere due metodi con stessi parametri di input ma con diverso parametro di output per intenderci in C# uno void e l’altro int. A garantire il corretto funzionamento dell’Overload sarà il compilatore che determinerà quale dei metodi verrà invocato sulla base del numero e del tipo dei parametri inviati.