Compatibile con Xcode 8

Ereditarietà delle Classi. Dai piena potenza alla programmazione ad oggetti

La programmazione ad oggetti, che sia con il linguaggio Swift o con altri, risente di uno dei conflitti umani più antichi della storia. Un conflitto che parte unicamente da una domanda: Cos’hanno in comune e diverso Uomo e Donna?

Può sembrare un qualcosa che non c’azzecca niente con la programmazione, eppure la sua risposta ha delle ripercussioni che ti trascinerai dietro fino allo sviluppo delle applicazioni. Se ti va puoi già cominciare a dare uno sguardo al corso successivo.

ereditarieta-e-subclass-linguaggio-swift

Se dovessi rappresentare Donna e Uomo con la programmazione ad oggetti sicuramente creeresti due classi diverse. Perché? Perché hanno attributi e metodi differenti (per pura semplificazione, basta pensare ai gioielli di entrambe le categorie).

Però in alcune cose queste classi si somiglierebbero. Perché avranno sicuramente attributi comuni che appartengono, in generale, all’essere umano o alla persona.

ereditarieta-programmazione-ad-oggetti-linguaggio-swift

Il problema, se volessi fare un altro esempio, è simile a quello del Cane e del Gatto. Senza alcun dubbio sono diversi ma, al tempo stesso, fanno parte della categoria degli Animali.

Peppe, dove vuoi arrivare?

Voglio arrivare alla seguente domanda: Esiste un modo per accomunare più classi e contemporaneamente distinguerle tra loro?

Nel caso della classe Uomo e della classe Donna, potremmo dire che entrambe prendono delle proprietà da un’ipotetica classe generica chiamata Persona. Allo stesso modo, il Cane e Gatto, si differenziano ma sono comunque degli Animali (cioè prendono delle proprietà comuni da una classe, non ancora definita, chiamata Animale).

Anche con la programmazione ad oggetti sarai in grado di poter definire una o più classi come discendenti da una classe più generica. Cioè proprio come nella realtà, anche le classi potranno essere definite a partire da un modello generico.

Questa caratteristica della programmazione OOP (Object Oriented Programming) prende il nome di Ereditarietà. L’Ereditarietà o Inheritance permette di creare classi, partendo da una classe più generica, facendoti evitare di dover riscrivere codice ridondante in classi diverse. 

Sei pronto a scoprire l’Ereditarietà classi linguaggio Swift? Allora cominciamo!

ereditarieta-delle-classi-linguaggio-swift

Superclasse e Sottoclasse

La risoluzione consiste nel creare una classe, detta padre o super, che racchiude gli attributi e metodi comuni a tutti gli oggetti di una categoria e tante classe figlie che, ereditano le caratteristiche e contemporaneamente aggiungono quel quid in più che le contraddistingue dal padre e dagli altri fratelli.

In questo modo, il problema del gatto e del cane si risolverebbe rendendo le rispettive classi figlie della superclasse Animale e specializzando le rispettive classi con qualcosa che le faccia distinguere sia dai fratelli (altre tipologie di animali) che ovviamente dal genitore.

Ora, cerca di fare mente locale perché tutto lo sviluppo delle applicazioni si basa su questo semplice meccanismo padre-figlio.

Padre o Superclasse

La classe padre è quella che accomuna tutti gli oggetti di una stessa categoria. Fai attenzione perché ho detto accomuna!

Se prendiamo il Cane ed il Gatto, la classe Animale potrebbe fare da superclasse:

Così com’è la classe può descrivere benissimo qualsiasi tipo di animale: un cane, un gatto, un cavallo, un pesce.

Nella pratica la superclasse descrive il comportamento generale dei figli, non fa distinzioni iniziali e cerca di essere quanto più possibile imparziale per poter soddisfare tutti i loro requisiti.

Figli o Sottoclasse

Figlio lo sono io e lo sei tu. Come tali, ereditiamo dai nostri genitori alcune caratteristiche che ci fanno assomigliare a loro e altre che ci differenziano.

Un figlio è una specializzazione del padre. Si dice sempre che l’allievo supera il maestro o che il figlio cerca di ampliare quello che il padre ha creato e deve essere così sia nella vita che nella programmazione ad oggetti.

Una sottoclasse o subclass eredita tutti i metodi e attributi. in più può aggiungerne di nuovi e modificare quelli esistenti. 

Se lo hai capito teoricamente, l’atto pratico sarà ancor più semplice. Prima però, devo introdurti uno strumento fondamentale per qualsiasi sviluppatore.

L’importanza dei diagrammi

Un diagramma delle classi, quando si parla di programmazione ad oggetti, è essenziale per la buona progettazione di una qualsiasi applicazione.

In genere si utilizzano delle sintassi e regole ben precise per rappresentare graficamente le classi (vedi UML), io mi limiterò a rappresentartele così:

padre-e-figlio-ereditarieta-classi

La classe genitore o superclasse va messa in testa al grafico, sotto seguono le classi figlie che sono collegate con una freccia inversa alla classe genitore. Perché freccia inversa?

Una classe figlia ti ricordo che è una specializzazione della classe padre, quindi in realtà i diagrammi come gli stessi oggetti andrebbero letti in chiave diversa dato che una subclass (classe figlia) è più specifica rispetto al padre.

Se ci pensi, anche nella realtà accade la stessa cosa, infatti dici che: Il Cane è un Animale e non l’Animale è un Cane! La differenza è filosofica ma il principio che ci sta dietro ha riscontri estremamente pratici.

Nota anche che le classi Cane e Gatto viaggiano sulla stessa linea, infatti sono una accanto all’altro e questo perché entrambe svolgono lo stesso ruolo concettuale.

Sia il Cane che il Gatto non sono altro che altre classi padre che, pur essendo figlie della classe Animale, circoscriveranno un gruppo ben preciso di altri nuovi oggetti. Pensa ad esempio alle razze dei Cani e quelle dei Gatti:

Il gioco delle sottoclassi è sempre lo stesso!

Si parte sempre dalla creazione di una classe generale e che racchiude una categoria quanto più ampia possibile ma sempre attinente ad un unico argomento.

Successivamente si passa al primo livello di sottoclasse e, se il problema lo richiede, si continua a specializzare l’albero aggiungendo nuove sottoclassi e nuovi livelli.

Primo Recap

Prima di vedere l’implementazione con il linguaggio Swift è giusto fare il punto della situazione:

  • Una superclasse o classe padre descrive il comportamento e look di una categoria ampia di oggetti non specializzati.
    • Un esempio di superclasse potrebbe essere la Persona, infatti una Persona ha attributi che vanno bene sia per la Donna che per per l’Uomo ma non fa distinzione tra i due.
    • Per capire se una classe è o meno una super classe sempre di fare il gioco della domanda, “Una Donna è una Persona? o Una Persona è una Donna?” ovviamente la prima domanda è vera, la secondo è falsa.
  • Una classe figlia o subclass è una specializzazione della superclass. Questo vuol dire che oltre ad ereditare tutto ciò che ha il padre, potrà aggiungere cose nuove e modificare quelle esistenti.
    • Per rimanere il linea con il tema precedente, un esempio di classi figlie della classe Persona potrebbero essere Uomo e Donna. Entrambe ereditano caratteristiche comuni come il colore degli occhi, l’altezza, il nome ecc ma si distinguono per tratti unici.

Creare una Subclass

Fatta questa breve, ma intensa, introduzione è arrivato il momento di passare all’atto pratico per analizzare come implementare una struttura di questo genere nel tuo playground e successivamente nelle tue future applicazioni.

Let’s start!

Comincia definendo la classe Animale, ma prima poniti alcuni quesiti tecnici e strutturali.

Quali attributi/metodi deve possedere la classe padre? gli attributi e metodi che sto per definire, andranno bene per tutte le classi figlie?

Se la seconda domanda non viene rispettata allora vuol dire che stai sbagliando la progettazione della classe.

Nel linguaggio Swift, come il qualsiasi altro, un figlio deve poter ereditare tutti gli attributi/metodi di una classe padre e deve essere in grado di utilizzarli tutti!

Seguendo questa semplice regola, la classe Animale dovrebbe essere qualcosa del genere:

Infatti, qualsiasi figlio che creerai da questa classe potrà utilizzare tutti i metodi e attributi senza scartarne alcuno. Puoi fare la prova cercando di vedere, attributo per attributo e metodo per metodo, se la superclasse va bene per qualsiasi tipo di Animale che ti viene in mente.

La creazione di un oggetto animale avviene come hai già imparato nella lezione sulla programmazione ad oggetti:

Ora immagina di voler creare la famosa classe Cane come figlia della classe Animale.

Per rendere una classe come subclass di un’altra devi mettere, dopo il nome della classe, due punti ed il nome della classe che vuoi ereditare:

Il seguente esempio definisce una subclass, chiamata Cane, partendo dalla superclass Animale. La classe appena definita, prende tutti i valori e metodi della classe padre (come nome, età, ecc.).

Secondo il principio dell’ereditarietà, all’interno della classe figlia potrai scrivere nuovi metodi e attributi:

Proprietà e metodi della superclass

Qui avviene il primo miracolo.

Dato che si tratta di una subclass, all’atto della creazione, potrai utilizzare il metodo init della classe genitore. Ed ovviamente, potrai anche utilizzare e modificare i metodi e proprietà della classe genitore:

 

Esercizio 0. Prova a definire la subclass Gatto provando ad aggiungere delle personalizzazioni.

Inizi ad apprezzare questo tipo di programmazione? Ancora no? Aspetta perché ancora non sei arrivato sul più bello!

condividi xcoding

Ho impiegato un po’ di tempo a scrivere questa lezione, aiutami semplicemente mettendo un mi piace o follow alle mie pagine.

[addtoany]

Grazie davvero :-)

 

Overriding di una Classe

La spettacolarità dell’ereditarietà è che ti è permesso modificare i metodi o le proprietà della class genitore. Questo aspetto si chiama Overriding (cioè sovrascrivere).

L’Overriding è utile in tutti quei casi in cui una superclasse porta con se un metodo generico che hai bisogno di modificare nella subclass.

Ti faccio subito un esempio.

Riprendiamo l’esempio della classe Animale e Cane. Ho aggiunto, alla classe Animale, un metodo verso() vuoto dato che non possiamo definire a priori il verso di un animale (ho tolto anche delle proprietà per semplificare l’esercizio):

Adesso vogliamo che la classe Cane prenda quel metodo e lo modifichi per stampare in console “bau bau” quando lo richiamerà.

Puoi modificare il comportamento di un metodo della superclass scrivendo il metodo, all’interno della classe figlia, aggiungendo il prefisso override. 

In questo modo stai dicendo al compilatore: “Ehi senti! Non sto creando un nuovo metodo, sto solo ridefinendo il comportamento del metodo padre”:

Senza l’override avresti dovuto creare il metodo all’interno della classe figlio. Cosa che avresti potuto ovviamente fare. Solamente che, per quanto detto in tutte queste lezioni, sarebbe stato controproducente dato che il nostro obiettivo è quello di semplificare e astrarre il codice quanto più possibile.

Mettere i metodi generali all’interno della classe super ti da la certezza che tutti i figli utilizzeranno quel metodo per svolgere quel particolare compito. Procedimento utilissimo nel caso di applicazioni che hanno una logica sempre uguale e contenuti diversi o per tutti quei casi in cui ti serve condividere il progetto con altri sviluppatori.

Overriding completo

La classe Animale ha un metodo chiamato stampaDati() che già esegue delle operazioni. Vogliamo che le classi figlie ereditino questo metodo e lo sistemino aggiungendo delle nuove cose da stampare:

Con l’override effettuato dalla classe Cane, quello che viene stampato non è l’unione dei due messaggi, bensì solo il messaggio contenuto nel metodo della subclass.

Questo accade perché un override, così per come l’hai conosciuto, sostituisce in toto il comportamento del metodo della superclass.

C’è un modo per evitarlo?

Overriding parziale e keyword super

Avevo detto ad inizio lezione che la classe figlia eredita tutto e per tutto intendevo anche i il corpo dei metodi.

Per poter usufruire delle funzionalità offerte dal metodo padre è necessario richiamarlo esplicitamente utilizzando la parola chiave super, seguita da un punto e dal nome del metodo in questione.

La keyword super farà in modo di eseguire il metodo così come si trova nella classe genitore. Sotto, infine, potrai aggiungere le tue personalizzazioni:

Adesso, il cane, quando richiamerà il metodo stampaDati() vedrà sia le print definite nel metodo della classe genitore e sia i print definiti nell’override.

In generale si utilizza il super principalmente per richiamare i metodi della classe genitore così per come si trovano nello stato originale e prima della modifica che andrai a fare nell’ovveride.

Init e override

Cosa succede nel caso in cui la subclass ha dei nuovi metodi che devono essere inizilizzati in fase di costruzione dell’oggetto?

Prendiamo un’ipotetica classe Persona ed una subclass Utente utilizzata per gestire i dati degli utilizzatori dell’applicazione:

La classe Utente ha due nuovi attributi, password ed email, che devono essere inizializzati dall‘init dato che non hanno un valore di default.

Dato che Utente eredita dalla classe Persona è come se avesse anche le proprietà nome cognome. Un ipotetico nuovo init dovrebbe tener conto di tutte le proprietà senza valori di default, cioè di quelle presenti sia nella subclass che nella superclass.

Dato che le proprietà nome e cognome sono presenti nella superclass non puoi accedervi utilizzando la sintassi self.nome e self.cognome perché non sono dentro self (cioè la classe corrente).

In tuo soccorso puoi richiamare il metodo init della classe super. Prima di farlo, però, devi passare i valori alle proprietà della classe self.

Quindi, prima inizializzo le proprietà della classe self (corrente) e successivamente richiamato il costruttore della classe super per inizializzare le sue proprietà.

override-classi-linguaggio-swift

4 Regole da non dimenticare

  1. La sottoclasse eredita tutti i metodi e attributi della classe super implicitamente.
  2. La sottoclasse può avere nuovi attributi e metodi rispetto alla superclasse.
  3. Una sottoclasse può ridefinire metodi, specializzando quelli della super classe (Meccanismo di Overriding).
  4. Una sottoclasse deve essere usabile in qualsiasi caso possa essere utilizzata la superclasse.

Considerazioni

Una delle domande che mi viene sempre posta è: perché dovrei usare l’ereditarietà?

Quando arriverai a creare le tue prime applicazioni vedrai che tutti gli strumenti che ti saranno forniti (bottoni, immagini ecc) sono gestiti da classi. Queste classi non potranno essere modificate perché sono conservate all’interno di spazi speciali (chiamati framework) non accessibili.

L’unico strumento che avrai, per esempio per modificare l’aspetto di un bottone, sarà quello di creare una subclass della classe genitore. Quindi, capire il significato dell’ereditarietà non è solamente un puro aspetto didattico. Tutto il mondo delle applicazioni gira intorno all’ereditarietà.

Inoltre, non smetterò mai di ripeterlo, l’ereditarietà delle classi, è un modo elegante e funzionale per ottimizzare il codice in vista del riutilizzo in altri progetti e applicazioni.

Esercizio della lezione

Un sito web di notizie online ti ha richiesto la creazione di un’applicazione per la visualizzazione nativa dei suoi articoli e feed delle notizie.

  • Tutte le notizie hanno un titolo, un testo, la data di pubblicazione e l’autore.
  • L’Autore è definito a partire da un nome, cognome e da un indirizzo web.

Le notizie oltre ad essere di tipo testuale possono essere di tipo Video. Queste si differenziano perché posseggono un url del video e la durata del video (oltre alle proprietà classiche).

Quello che devi creare è la struttura delle classi del progetto ed una sorta di gestore delle notizie. Il gestore delle notizie è in grado di:

  • Aggiungere un nuovo articolo o video ad una collezione.
  • Restituire la lista di articoli o video dato il nome di un autore.
  • Calcolare il numero di video e articoli presenti.
  • Restituire l’ultimo articolo o video pubblicato (cioè inserito nel gestore).

Non ho inserito suggerimenti perché, a questo punto, dovresti essere in grado di risolvere l’esercizio utilizzando diversi strumenti. Ad ogni modo, nel caso avessi delle domande sull’esercizio, non esitare a scrivere un commento.

Buona Programmazione!

Changelog

  • 2/11/2016 – Aggiunto il changelog. Modifiche sostanziali al testo ed agli esercizi. Assicurata compatibilità con Xcode 8 e Swift 3.

Torna a: Corso gratuito linguaggio di programmazione Swift > Programmazione ad oggetti in Swift

Start typing and press Enter to search

Extension e Protocol in Swift