Il paradigma OOP non nasce soltanto per garantire una maggiore riutilizzabilità del codice, ma anche per favorire lo sviluppo collaborativo, le classi sono infatti per loro natura "condivisibili". A ben vedere si tratta comunque di concetti strettamente legati. Il fatto che l’approccio alla programmazione ad oggetti favorisca il lavoro in team non significa però che qualsiasi componente di un gruppo debba avere lo stesso livello di accesso agli elementi del sorgente.
Diviene così utile un meccanismo come quello dell’incapsulamento che permette di rendere "privati", in sostanza inaccessibili, attributi specifici.
Incapsulamento in Python
L’incapsulamento (data encapsulation) è una pratica che consente di restringere l’accesso a variabili e metodi, in questo modo vengono impedite le modifiche dirette a carico dei dati. Quando si vuole definire un attributo privato in Python si utilizza l’operatore "_" (o anche "__") come prefisso da anteporre al nome dell’attributo stesso.
Il seguente esempio permette di analizzare un caso pratico di ricorso all’incapsulamento:
# Incapsulamento in Python
class Facciata:
def __init__(self):
self.__colore = "verde"
def colora(self):
print("La facciata è di colore {}".format(self.__colore))
def impostaColore(self, variante):
self.__colore = variante
# istanza di classe
x = Facciata()
x.colora()
# modifica del colore
x.__colore = "rosso"
x.colora()
# funzione setter
x.impostaColore("rosso")
x.colora()
L’output derivante dall’esecuzione del codice mostrato sarà il seguente:
La facciata è di colore verde La facciata è di colore verde La facciata è di colore rosso
Analizzando il listato è possibile osservare come l’applicazione preveda innanzitutto la definizione di una classe che prende il nome di "Facciata". Viene poi utilizzato il metodo __init__() per archiviare l’informazione relativa al colore.
Al funzionamento dello script partecipano due metodi definiti in fase di programmazione: "colora()", con il compito di stampare una stringa contenente il dato precedentemente archiviato, e "impostaColore()", deputato ad introdurre una variante cromatica.
Una volta effettuata l’istanza di classe, si è provato a modificare l’informazione relativa al colore, tale tentativo non ha però avuto l’effetto atteso perché l’attributo "__colore" viene considerato privato dall’interprete di Python.
L’unico modo per modificare il dato è invece quello di ricorrere ad una funzione setter, nel nostro caso impostaColore(), cioè un metodo pubblico di classe che quando chiamato consente di recuperare il valore di un attributo o di modificarlo.
Polimorfismo in Python
Tecnicamente si può definire il polimorfismo come una tecnica che prevede l’utilizzo della medesima interfaccia per manipolare più tipologie di tipi di dato, anche in questo caso si parla di una funzionalità particolarmente utile sia per il riutilizzo del codice che per lo sviluppo collaborativo.
Si ipotizzi di voler guidare uno scooter, esistono tantissimi modelli di scooter ma indipendentemente da questa differenza è possibile guidarli tutti nello stesso modo. Il concetto di polimorfismo si riassume con questo semplice esempio, un’interfaccia è infatti un tramite per l’interazione con un elemento e il polimorfismo consente di definire interfacce comuni per oggetti di classi diverse.
Come di consueto, la creazione di una semplice applicazione consentirà di chiarire ulteriormente quanto espresso fino ad ora:
# Polimorfismo in Python
class Persiano:
def miagola(self):
print("Il mio persiano miagola")
def graffia(self):
print("Il mio persiano non graffia")
class Siamese:
def miagola(self):
print("Il mio siamese non miagola")
def graffia(self):
print("Il mio siamese graffia")
# interfaccia comune
def evento(gatto):
gatto.miagola()
# istanza oggetto
tyson = Persiano()
foreman = Siamese()
# passaggio degli oggetto all'interfaccia comune
evento(tyson)
evento(foreman)
L’output generato dall’esecuzione dello script sarà il seguente:
Il mio persiano miagola Il mio siamese non miagola
Come è possibile osservare, l’esempio precedente presenta due classi, "Persiano" e "Siamese". Ad entrambe le classi fanno riferimento due metodi omonimi, "miagola()" e "graffia()", che hanno il compito di stampare delle stringhe di testo, l’unica differenza sta nel fatto che a seconda della classe le stringhe da visualizzare a video saranno diverse.
Per applicare il polimorfismo si è fatto però ricorso ad un’interfaccia comune, "evento()", in grado di manipolare qualsiasi oggetto, indipendentemente dalla classe in cui avviene l’istanza. Ecco perché quanto atteso dai metodi (nel caso specifico è stato preso in considerazione "miagola()"), viene effettivamente restituito tramite il passaggio delle istanze "tyson" e "foremean" all’interfaccia.