Guida al Visual Basic .NET
Capitolo 29° - LEreditarieta
Eccoci arrivati a parlare degli aspetti peculiari di un linguaggio ad oggetti! Iniziamo con l'Edereditarietà. L'ereditarietà è la possibilità di un linguaggio ad oggetti di far derivare una classe da un'altra: in questo caso, la prima assume il nome di classe derivata, mentre la seconda quello di classe base. La classe derivata acquisisce tutti i membri della classe base, ma può ridefinirli o aggiungerne di nuovi. Questa caratteristica di ogni linguaggio Object Oriented è particolarmente efficace nello schematizzare una relazione "is-a" (ossia "è un"). Per esempio, potremmo definire una classe Vegetale, quindi una nuova classe Fiore, che eredita Vegetale. Fiore è un Vegetale, come mostra la struttura gerarchica dell'ereditarietà. Se definissimo un'altra classe Primula, derivata da Fiore, diremmo che Primula è un Fiore, che a sua volta è un Vegetale. Quest'ultimo tipo di relazione, che crea classi derivate che saranno basi per ereditare altre classi, si chiama ereditarietà indiretta. Passiamo ora a vedere come si dichiara una classe derivata: Class [Nome] Inherits [Classe base] 'Membri della classe End ClassLa keyword Inherits specifica quale classe base ereditare: si può avere solo UNA direttiva Inherits per classe, ossia non è possibile ereditare più classi base. In questo frangente, si può scoprire come le proprietà siano utili e flessibili: se una classe base definisce una variabile pubblica, questa diverrà parte anche della classe derivata e su tale variabile verranno basate tutte le operazioni che la coinvolgono. Siccome è possibile che la classe derivata voglia ridefinire tali operazioni e molto probabilmente anche l'utilizzo della variabile, è sempre consigliabile dichiarare campi Private avvolti da una proprietà, poichè non c'è mai alcun pericolo nel modificare una proprietà in classi derivate, ma non è possibile modificare i campi nella stessa classe. Un semplice esempio di ereditarietà: Class Person 'Per velocizzare la scrittura del codice, assumiamo che 'questi campi pubblici siano proprietà Public FirstName, LastName As String Public ReadOnly Property CompleteName() As String Get Return FirstName & " " & LastName End Get End Property End Class 'Lo studente, ovviamente, è una persona Class Student 'Student eredita da Person Inherits Person 'In più, definisce anche questi campi pubblici 'La scuola frequentata Public School As String 'E l'anno di corso Public Grade As Byte End ClassIn seguito, si può utilizzare la classe derivata come si è sempre fatto con ogni altra classe. Nel farne uso, tuttavia, è necessario considerare che una classe derivata possiede non solo i membri che il programmatore ha esplicitamente definito nel suo corpo, ma anche tutti quei membri presenti nella classe base che si sono implicitamente acquisiti nell'atto stesso di scrivere "Inherits". Se vogliamo, possiamo assimilare una classe ad un insieme, i cui elementi sono i suoi membri: una classe base è sottoinsieme della corrispondente classe derivata. Di solito, l'ambiente di sviluppo aiuta molto in questo, poiché, nei suggerimenti proposti durante la scrittura del codice, vengono automaticamente inserite anche le voci ereditate da altre classi. Ciò che abbiamo appena visto vale anche per ereditarietà indiretta: se A eredita da B e B eredita da C, A disporrà dei membri di B, alcuni dei quali sono anche membri di C (semplice proprietà transitiva). Ora, però, bisogna porre un bel Nota Bene alla questione. Infatti, non tutto è semplice come sembra. Forse nessuno si è chiesto che fine fanno gli specificatori di accesso quando un membro viene ereditato da una classe derivata. Ebbene, esistono delle precise regole che indicano come gli scope vengono trattati quando si eredita:
Esempio: Module Esempio Class Person 'Due campi protected Protected _FirstName, _LastName As String 'Un campo private readonly: non c'è ragione di rendere 'questo campo Protected poichè la data di nascita non 'cambia ed è sempre accessibile tramite la proprietà 'pubblica BirthDay Private ReadOnly _BirthDay As Date Public Property FirstName() As String Get Return _FirstName End Get Set(ByVal Value As String) If Value <> "" Then _FirstName = Value End If End Set End Property Public Property LastName() As String Get Return _LastName End Get Set(ByVal Value As String) If Value <> "" Then _LastName = Value End If End Set End Property Public ReadOnly Property BirthDay() As Date Get Return _BirthDay End Get End Property Public ReadOnly Property CompleteName() As String Get Return _FirstName & " " & _LastName End Get End Property 'Costruttore che accetta tra parametri obbligatori Sub New(ByVal FirstName As String, ByVal LastName As String, _ ByVal BirthDay As Date) Me.FirstName = FirstName Me.LastName = LastName Me._BirthDay = BirthDay End Sub End Class 'Lo studente, ovviamente, è una persona Class Student 'Student eredita da Person Inherits Person 'La scuola frequentata Private _School As String 'E l'anno di corso Private _Grade As Byte Public Property School() As String Get Return _School End Get Set(ByVal Value As String) If Value <> "" Then _School = Value End If End Set End Property Public Property Grade() As Byte Get Return _Grade End Get Set(ByVal Value As Byte) If Value > 0 Then _Grade = Value End If End Set End Property 'Questa nuova proprietà si serve anche dei campi FirstName 'e LastName nel modo corretto, poichè sono Protected anche 'nella classe derivata e fornisce un profilo completo 'dello studente Public ReadOnly Property Profile() As String Get 'Da notare l'accesso a BirthDay tramite la proprietà 'Public: non è possibile accedere al campo _BirthDay 'perchè è privato nella classe base Return _FirstName & " " & _LastName & ", nato il " & _ BirthDay.ToShortDateString & " frequenta l'anno " & _ _Grade & " alla scuola " & _School End Get End Property 'Altra clausola importante: il costruttore della classe 'derivata deve sempre richiamare il costruttore della 'classe base Sub New(ByVal FirstName As String, ByVal LastName As String, _ ByVal BirthDay As Date, ByVal School As String, _ ByVal Grade As Byte) MyBase.New(FirstName, LastName, BirthDay) Me.School = School Me.Grade = Grade End Sub End Class Sub Main() Dim P As New Person("Pinco", "Pallino", Date.Parse("06/07/90")) Dim S As New Student("Tizio", "Caio", Date.Parse("23/05/92"), _ "Liceo Classico Ugo Foscolo", 2) Console.WriteLine(P.CompleteName) 'Come si vede, la classe derivata gode degli stessi membri 'di quella base, acquisiti secondo le regole 'dell'ereditarietà appena spiegate Console.WriteLine(S.CompleteName) 'E in più ha anche i suoi nuovi membri Console.WriteLine(S.Profile) 'Altra cosa interessante: dato che Student è derivata da 'Person ed espone tutti i membri di Person, più altri, 'non è sbagliato assegnare un oggetto Student a una 'variabile Person P = S Console.WriteLine(P.CompleteName) Console.ReadKey() End Sub End ModuleL'output: Pinco Pallino Tizio Caio Tizio Caio, nato il 23/5/1992 frequenta l'anno 2 alla scuola Liceo Classico Ugo Foscolo Tizio Caio(Per maggiori informazioni sulle operazioni con le date, vedere il capitolo 57) Anche se il sorgente è ampiamente commentato mi soffermerei su alcuni punti caldi. Il costruttore della classe derivata deve sempre richiamare il costruttore della classe base, e questo avviene tramite la keyword MyBase che, usata in una classe derivata, fa riferimento alla classe base corrente: attraverso questa parola riservata è possibile anche raggiungere i membri privati della classe base, ma si fa raramente, poichè il suo impiego più frequente è quello di riprendere le vecchie versioni di metodi modificati. Il secondo punto riguarda la conversione di classi: passare da Student a Person non è, come potrebbe sembrare, una conversione di riduzione, poichè durante il processo, nulla va perduto nel vero senso della parola. Certo, si perdono le informazioni supplementari, ma alla classe base queste non servono: la sicurezza di eseguire la conversione risiede nel fatto che la classe derivata gode degli stessi membri di quella base e quindi non si corre il rischio che ci sia riferimento a un membro inesistente. Questo invece si verifica nel caso opposto: se una variabile di tipo Student assumesse il valore di un oggetto Person, School e Grade sarebbero privi di valore e ciò generebbe un errore. Per eseguire questo tipo di passaggi è necessario l'operatore DirectCast.
C#, TypeScript, java, php, EcmaScript (JavaScript), Spring, Hibernate, React, SASS/LESS, jade, python, scikit, node.js, redux, postgres, keras, kubernetes, docker, hexo, etc...
|