La programmazione ad oggetti, basi e concetti fondamentali - parte 2

La programmazione ad oggetti, basi e concetti fondamentali - parte 2

Di Francesco Grotta


sviluppo web programmazione programmazione ad oggetti

Nello scorso articolo, abbiamo visto i primi concetti della programmazione ad oggetti, in particolar modo l'incapsulamento e l'astrazione.

In questa seconda parte vedremo invece l'ereditarietà ed il polimorfismo.

Ereditarietà

L’ereditarietà costituisce un altro principio fondamentale della programmazione ad oggetti. In generale essa rappresenta un meccanismo che consente di definire delle classi figlie che ereditano dalle classi padre tutti gli attributi. Come un figlio eredita dal padre le caratteristiche fisiche (tramite il DNA ) o il cognome, così nella programmazione il concetto di ereditarietà è molto simile.

Si definisce "classe figlia" quella che eredita tutte o parte delle proprietà e dei metodi definiti nella classe padre. In questo modo, si viene a formare una gerarchia. La "classe figlio" riutilizza tutti i campi e i metodi della classe genitore, ma può implementare anche dei propri campi o della logica "personale", sia con nuovi metodi, sia sovrascrivendo quelli del padre. 

Un figlio è quindi una specializzazione del padre. Come l’allievo supera il maestro, nella programmazione ad oggetti la classe figlia, è una classe più dettagliata del padre.

Cosa non scontata, la gerarchia è infinita, quindi ogni classe figlio può diventare padre di un'altra classe.

Proviamo a fare un esempio:

Uno dei maggiori vantaggi derivanti dall’uso dell’ereditarietà è la maggiore facilità nella manutenzione del software. 

Infatti, rifacendoci all’esempio di prima, se qualcosa dovesse variare per l’intera gerarchia della classe dei "Persona", per esempio vogliamo aggiungere l'attributo "Età", sarà sufficiente modificare soltanto l’oggetto padre per consentire che tutti gli oggetti figli ereditino la nuova caratteristica. Lo stesso vale per un metodo, ovviamente. 

Proviamo a fare un esempio più reale al campo informatico per capire le potenzialità di questo concetto. Pensiamo ad un eCommerce diviso per categorie. Supponiamo che il nostro sito venda scarpe, calzini e accessori, una possibile divisione dei nostri prodotti potrebbe essere la seguente:

La divisione è abbastanza logica. Sopra tutti le classi abbiamo il "Prodotto". Questo ha alcune proprietà base, tra cui il codice, il nome, la descrizione, un'immagine ed il prezzo. Sono tutte caratteristiche di cui avremo bisogno in tutti i prodotti e quindi è consigliato inserirle nel padre comune a tutte le classi prodotto. Poi dividiamo i prodotti che hanno una misura in formato numerico come le scarpe o i calzini. A pari livello abbiamo i prodotti che non hanno misure.

Se un domani volessimo aggiungere a tutti i prodotti anche l'attributo materiale, non dovremo fare altro che inserirlo nella classe Prodotti, oppure nel caso volessimo aggiungere una nuova tipologia di prodotti, potremo creare una nuova classe figlia.

Ovviamente gli attributi sono solo esemplificativi e permettono di avere una idea di come modellare le classi. In un caso reale potrebbe essere necessaria una divisione differente. Ciò nonostante è semplice capire la potenzialità sulla manutenzione del codice e del software o sulla semplicità di implementare nuove funzioni o comportamenti su più oggetti nello stesso momento, utilizzando una gerarchia.

Polimorfismo

La parola polimorfismo, letteralmente, indica la possibilità per uno stesso oggetto di avere più forme o più caratteristiche contemporaneamente. In informatica si traduce nel poter avere multiple implementazioni dello stesso metodo in base alla classe che stiamo utilizzando o in base ai parametri che passiamo al metodo. 

Prendiamo un esempio, per quanto banale, che rende bene l'idea. Se prendiamo gli esseri viventi, ci sono una miriade di modi di spostarsi. Pensiamo ad un uccello, ad un pesce, ad un serpente ed un cane. Tutti e quattro gli animali si sanno muovere (supponiamo avanti) ma ognuno di loro lo fa in modo radicalmente differente. Potremmo quindi pensare che tutti gli essere "Viventi" implementino il metodo "Muoversi", ma ognuno ha una implementazione differente. Il pesce nuoterà, l'uccello volerà, il serpente striscerà ed in fine il cane camminerà. Tutti espletano lo stesso obiettivo, ma lo fanno in modo unico.

La parte importante è che non è il programmatore che deve dire al sistema quale implementazione usare nei vari casi, ma sarà il sistema stesso ad utilizzare l'implementazione corretta a seconda dell'oggetto (istanza della classe) che starà gestendo in quel momento.

In un sistema non ad oggetti, o comunque che non è in grado di utilizzare il polimorfismo, un simile comportamento necessiterebbe, dal punto di vista del codice, di un costrutto tipo switch o di una serie di condizioni if/else if/else.

Torniamo all'esempio del nostro e-commerce.

Il nostro prodotto potrebbe avere un metodo che ritorna l'immagine principale del prodotto, quella utilizzata nella galleria prodotti o nelle anteprime. Di default questo metodo potrebbe semplicemente tornare la prima immagine disponibile, o quella più grande (ad alta definizione). Il poliformismo ci permette di poter sovrascrivere, o meglio fare override, dell'implementazione dove ci serve una implementazione più specifica o diversa. 

Nel nostro caso vogliamo fare l'override del metodo solo nella classe "Scarpe", in modo che ritorni come immagine principale l'immagine della scarpa vista di lato.

Creando una gerarchia (tramite l’ereditarietà) e definendo le classi in questo modo, ci assicuriamo che tutti i prodotti abbiano sempre implementato il metodo "getMainImage", che quindi potrà sempre essere richiamato in qualunque punto del nostro software, ma nel caso sia necessario, nelle singole classi è possibile implementare la logica specifica che meglio si adatta al nostro business o al nostro caso specifico.

Le potenzialità di questo approccio sono infinite, in quanto ci permette di definire molto semplicementi dei comportamenti o degli attributi comuni a più oggetti, collegati in modo gerarchico, ma allo stesso tempo, permettere di definire comportamenti specifici qualora ce ne sia bisogno.

Ovviamente quando si usa il poliformismo combinato con l’ereditarietà, l’implementazione che vince è quella più vicina alla classe che il sistema sta elaborando. Prendiamo l’esempio dell’e-commerce, quando chiameremo il metodo getMainImage della classe "Scarpe" utilizzeremo il metodo definito dalla classe padre "Prodotti Con Misure", ma se invece utilizzeremo il metodo su un oggetto di tipo "Calzini", andremo ad utilizzare la versione più specifica ridefinita nella classe stessa.

Impara a programmare in 3 mesi con il Corso di Coding Hackademy su Laravel PHP

Diventa Sviluppatore web in 3 mesi

Scopri il coding bootcamp Hackademy

Programma Completo