Con l’articolo di oggi non intendo scrivere un trattato sulla teoria della programmazione ad oggetti, non ne sarei capace e gli scaffali delle librerie sono pieni di libri su questo argomento…vorrei invece, con l’aiuto di qualche esempio, far vedere come i pilastri della OOP vengono implementati in objc e come la conoscenza di questi concetti basilari sia indispensabile per interpretare correttamente le guide fornite da Apple.
Ma quali sono questi pilastri della OOP? Sono l’ereditarietà, l’incapsulamento ed il polimorfismo, vediamo brevemente cosa sono e come applicarli creando una nostra classe inventata, la classe “persona“.
Creiamo una nuova classe cliccando sul menù file-> new file e selezionando dalla categoria “cocoa touch class” l’icona obiettive-c class. Notiamo la voce “subclass of: NSObject” questo significa che la nostra classe erediterà direttamente da NSObject, la madre di tutte le classi, se non è chiaro lo sarà più avanti quando parleremo di ereditarietà.
Diamo un nome significativo (“fraction“) e clicchiamo su ok. I file creati hanno questa sintassi, io li ho arricchiti con due variabili di istanza e 5 metodi:
//file fraction.h
@interface Fraction: NSObject {
int numerator;
int denominator;
}
-(void) print;
-(void) setNumerator: (int) n;
-(void) setDenominator: (int) d;
-(int) numerator;
-(int) denominator;
@end
//file fraction.m
@implementation Fraction
-(void) print {
printf( "%i/%i", numerator, denominator );
}
-(void) setNumerator: (int) n {
numerator = n;
}
-(void) setDenominator: (int) d {
if (d!=0)
denominator = d;
}
-(int) denominator {
return denominator;
}
-(int) numerator {
return numerator;
}
@end
La suddivisione in due file .h e .m ha il solo scopo di separare formalmente la “descrizione” di una classe dalla sua implementazione. La “descrizione” di una classe in OOP si chiama “interfaccia” ed è costituita dall’elenco dei metodi e, se ci sono, l’elenco delle variabili di istanza. Questo è tutto quello che serve ad un ipotetico utilizzatore della nostra classe, il quale la potrà utilizzare come una semplice blackbox che esegue quanto specificato nell’interfaccia, senza curarsi dell’implementazione. Nel nostro caso l’utilizzatore della nostra classe non avrà accesso diretto alle variabili numerator e denumerator se non utilizzando gli appositi metodi setNumerator e setDenumerator. Questo ci ha dato la possibilità di inserire un controllo sul valore del denominatore che sarebbe stato pressocchè impossibile altrimenti.
Questo è il primo pilastro della OOP, ovvero l’incapsulamento, l’arte del nascondere l’implementazione. In realtà la faccenda è un pò più complicata di così, perchè la presenza di opportuni modificatori (@public, @private , @protected etc) permettono di variare la visibilità delle variabili di istanza e dei metodi, ma al momento ci accontentiamo di sapere che fornire una corretta interfaccia alla classe e nascondere l’implementazione assicura versatilità e possibilità di riutizzo del codice.
Dopo il nome della classe ed i due punti troviamo il nome della superclasse, in questo caso NSObject. Ma a che serve specificare una superclasse? Serve, banalmente, a sfruttare il lavoro già svolto. Supponiamo di aver già creato e testato la classe “persona” con tutti i suoi metodi quando ci commissionano un programma per gestire il personale di un ufficio, avremo probabilmente bisogno di aggiungere alcune proprietà alla classe persona, per esempio “reparto” oppure “livello” etc.
In questo caso entra in gioco l’ereditarietà, perchè piuttosto che creare ex novo la classe impiegato potremo semplicemente specificare che la sua superclasse è la classe persona.
Questo significa che impiegato eredita automaticamente da persona tutti i suoi metodi e tutte le variabili di istanza, senza scrivere una riga di codice! Supponendo per esempio che la classe persona abbia un metodo chiamato getCodicePersonale sarà possibile invocarlo automaticamente anche su oggetti di tipo impiegato.
Vi siete mai chiesti come sia possibile invocare su qualsiasi oggetto i metodi alloc, init, dealloc anche se in realtà non li abbiamo scritti? Semplice, perchè sono definiti nella classe NSObject dalla quale tutte le altre discendono, quindi vengono automaticamente ereditati.
E come mai su una UILabel posso invocare initWithFrame anche se nella class reference di UILabel non ce n’è traccia? anche in questo caso la colpa è dell’ereditarietà perchè se guardiamo bene vediamo che la classe UILabel è sottoclasse di UIView la quale definisce il metodo initWithFrame che quindi è automaticamente disponibile in tutte le sue sottoclassi.
Un ulteriore flessibilità nell’eritarietà è data dalla possibilità di effettuare l’override dei metodi della superclasse. Questo che vuol dire? Vuol dire che se il metodo getCodicePersonale della classe persona restituisce, ad esempio, il codice fiscale, nella classe impiegati lo stesso metodo può restituire il numero di matricola. Questo si ottiene semplicemente riscrivendo il metodo all’interno della sottoclasse.
Il terzo pilastro della programmazione ad oggetti è la possibilità di avere più metodi con lo stesso nome che svolgono compiti diversi, questa che a prima vista sempre un’assurdità si chiama polimorfismo, e permette ad esempio di dichiarare due metodi con lo stesso nome ma differente tipo di parametri con questa sintassi:
- (void) stampaSchedaCliente:(int)codCliente;
- (void) stampaSchedaCliente:(NSStrng *)nomeCliente;
a seconda del tipo di parametro che verrà passato al metodo sarà invocata l’implementazione corrispondente.

2 Responses to “L#009 – Programmazione Orientata agli Oggetti (OOP)”
29 Gennaio 2010
Giancarlo RomeoQuello che in questa guida viene descritto come Polimorfismo è in realtà un semplice Overloading…
29 Gennaio 2010
IgnaziocHai ragione, forse ho semplificato troppo la storia.
L’overloading è l’espressione di un particolare (il più semplice) tipo di polimorfismo.
Per chiarezza possiamo aggiungere che in oop generalmente vengono identificati 4 tipi di polimorfismo, due sono l’overloading e la coercion e vengono detti “ad hoc”, gli altri due sono detti “universali” e sono l’inclusione” ed il parametrico.