10. Corso completo di Programmazione Java – Introduzione all’Object Oriented Programming – Parte V

Introduzione all'Object Oriented Programming - Parte 5 La scorsa settimana abbiamo parlato di polimorfismo: senza bisogno di if riesco ad avere metodi che fanno la cosa opportuna in base al tipo su cui vengono invocate; abbiamo visto che il polimorfismo si basa sull’overriding e l’ereditarietà. Quest’oggi riprendiamo il nostro esempio delle forme geometriche e cominciamo a parlare di classi astratte.

Classi astratte

Nel post precedente abbiamo creato una gerarchia di ereditarietà: GraphicObject di cui avevamo due specializzazioni: Circle e Rectangle. In realtà il metodo draw, il nostro metodo polimorfico, non sapevamo definirlo, infatti la sua implementazione era abbastanza insensata:

public void draw() {
		System.out.println("Non so disegnare una forma");
	}

Questo succede anche nelle migliori famiglie: noi sappiamo che un tipo ha un comportamento, ma a quel livello non sappiamo definirlo e sarà definito nei sottotipi specializzati: sappiamo che nel nostro dominio un oggetto grafico sarà disegnato, ma per saperlo fare dobbiamo prima sapere di che oggetto grafico stiamo parlando (cerchio, rettangolo etc).

Metodi e classi astratte

In Java, così come in altri linguaggi di programmazione OOP, questa esigenza può essere coperta con i metodi atratti: un metodo di cui viene fornita solo la dichiarazione e non la definizione. Per questi metodi il body non è definito, la classe che li contiene è definita astratta anch’essa e non può essere istanziata. Le classi che estendono una classe astratta devono definire tutti i loro metodi astratti o essere definite a loro volta astratte.

Disegna con astrazione

Vediamo nel nostro caso come utilizzare questa feature dei linguaggi OOP: trasformiamo GraphicObject in una classe astratta e draw in un metodo astratto:

public abstract class GraphicObject {
	// impossibile sapere come si disegna una forma generica
	// tuttavia so che è un comportamento che voglio avere
	public abstract void draw();
}

Le classi Rectangle e Circle rimangono invariate. In molti casi le classi astratte rendono più naturale modellare alcune realtà, garantendo eleganza di design e efficienza implementativa. Ma ci si può spingere ben oltre nel definire cose astratte… Siete pronti ad approdare nel mondo etereo delle interfacce?

Le Interfacce

Possiamo in prima battura definire un’interfaccia come una classe super astratta, che contiene solo dichiarazioni di metodi e non definizioni. Definisce una serie di requisiti sulla firma dei metodi, una sorta di contratto da rispettare, senza entrare nello specifico dell’implementazione. Ora, io potrei continuare a parlare delle interfacce, cercando di darne tante definizioni cambiando le parole, ma in realtà questo concetto va semplicemente assorbito e assimilato col tempo, passiamo invece a vedere un esempio. Supponiamo che alcune delle forme geometriche siano spostabili e altre no: vogliamo modellare questo comportamento con un’interfaccia Moveable:

public interface Moveable {
	void move(double x, double y);
}

Supponiamo, per qualche motivo assurdo (il motivo è prettamente didattico 🙂 ) che nel nostro dominio applicativo si possano spostare solo i cerchi. La classe Circle, oltre a estendere GraphicObject, deve implementare l’interfaccia Moveable:

public class Circle extends GraphicObject implements Moveable  {
	private Point center;
	private double radius;
	...   ...
	@Override
	public void draw() {
		System.out.println("Disegno un Cerchio");
	}
 
	@Override
	public void move(double x, double y) {
		this.center.x += x;
		this.center.y += y;
	}
}

I metodi di un’interfaccia sono tutti astratti, quindi tutte le classi che la implementano devono farne l’override. A differenza dell’ereditarietà che è singola (ogni classe può estendere una sola classe) non ci sono vincoli per l’implementazione dell’interfaccia: ogni classe può estendere quante interfacce si desidera: la flessibilità delle interfacce è decisamente soddisfacente!

Tuttavia le interfacce non possono contenere implementazioni, quindi, a differenza delle classi astratte, non realizzano centralizzazione del codice. Nelle interfacce è possibile anche inserire costanti e ovviamente non è possibile istanziare interfacce:

Moveable m = new Moveable(); // ERRORE!

Le interfacce sono molto importanti in Java, come in altri linguaggi OOP: sono alla base della costruzione di framework e librerie e consentono di disaccoppiare tra loro moduli software, persino di ridurre vincoli e dipendenze: supponiamo che un team di sviluppo A debba utilizzare i servizi creati da un altro team B. Il team B potrebbe creare subito un insieme di interfacce, il team A le utilizzerebbe, non entrando in merito a quella che poi sarà l’implementazione specifica che andrà a usare. Se poi in futuro viene scritta una implementazione migliore di tali interfacce il team A, così come tutti gli eventuali utenti di tale libreria, non devono cambiare una linea di codice: non male, no?

Di questi ultimi aspetti parleremo in modo più approfondito quando vedremo le Java Collection: i progettisti che hanno scritto queste API hanno fatto un intenso uso di interfacce per i motivi di cui sopra.

Conclusioni

Bene, il nostro viaggio all’interno dell’OOP si può dichiarare concluso, almeno da un punto di vista teorico. Nei prossimi post vedremo altri aspetti di questo linguaggio di programmazione, tuttavia riprenderemo spesso quanto detto fin’ora, perché per utilizzare Java al meglio occorre aver compreso appieno le leggi che regolano questo tipo di programmazione (OOP).

Chiunque avesse dubbi o volesse approfondire qualcosa prima del prossimo post non esiti a postare le sue domande nel nostro forum, risponderò con piacere. Alla prossima!

By Manuele Piastra

Sono un Project Manager i cui skill tecnici sono focalizzati al momento sullo sviluppo di applicazioni JavaEE (application server IBM Websphere 7.0 utilizzando JSF (RichFaces), JPA (EclipseLink) ed EJB3) ed Android. Presso OmniaGroup ricopro il ruolo di Training Manager: seleziono il personale tecnico, mi occupo della sua crescita formativa organizzando Corsi e Workshop sia interni che esterni, molti dei quali hanno visto me come docente. Ho tenuto sette sessioni di un corso di formazione di 50 ore sulle tecnologie JavaEE su Application Server WebSphere IBM presso il nostro cliente principale, Coop Italia, dei cui sistemi informativi sono consulente di riferimento. Sono blogger su CoseNonJaviste, blog tecnico che si occupa di Android e Java. Consulta il mio Curriculum online per maggiori informazioni.