|
Les interfícies (interfaces) |
|
|
 |
Un dels mecanismes d'herència
més interessants i potents són les interfícies
(interfaces). Una interfície
és un tipus una mica especial de classe
que conté noms de mètodes,
llistes de paràmetres, tipus
de retorn, però sense el cos dels
mètodes. És un objecte
que té forma, però no té contingut.
La seva feina és dir: "Aquest aspecte que tinc, és
el que han de tenir les classes
que m'implementin".
És una maqueta, una especificació i res més.
Les interfícies són molt
útils per recollir les similituds entre classes
que no estan vinculades per herència
i per a donar plasticitat. Una classe
pot implementar més d'una interfície,
en un model que recull parcialment l'herència
múltiple de C++.
|
|
|
|
Tot declarant una interfície
(interface) |
|
|
 |
Imaginem que rebem un encàrrec del director de l'institut: necessita
una eina que li faci el recompte del nombre d'hores d'absència
al centre de qualsevol persona que hi treballa, sigui professor, alumne
o personal no docent.
En fer l'anàlisi de la comanda observem que disposem de tota la
informació necessària per a escriure el programa: tenim
diferents taules Access amb les llistes
de professors, alumnes i personal no docent i taules on hem anat enregistrant
totes i cadascuna de les hores d'absència.
Els formats, però, no són idèntics: no quantifiquem
al mateix lloc ni de la mateixa forma les hores d'absència d'un
professor que les d'un alumne o les d'un administratiu. Per tant, els
programes de recompte no podran ser iguals per a un professor, un alumne
o un membre del PAS.
Resoldrem la situació a través de la creació d'una
interfície, que ens simplificarà
una mica la feina i ens permetrà construir una solució elegant
i llegible. Seguirem els passos seguents:
- En primer lloc hem d'imaginar un usuari ideal, abstracte,
al qual se li ha de poder aplicar el mecanisme de càlcul d'hores
d'absència. A aquest usuari ideal li direm Usuari
i farem que tots els membres del nostre institut en formin part.
- Després crearem tres classes
diferents Professor, Alumne
i Pas, que s'ajustin a la definició
abstracta d'Usuari -és a dir, que implementin
la interfície Usuari- i que
resolguin, cadascuna de la seva forma, la manera de recomptar les absencies.
- Seguidament farem una classe
ControlAbsencies que servirà per a
recomptar absencies, sigui quin sigui el tipus d'usuari
- Finalment escriurem un programa LesAbsenciesDe
que aplica ControlAbsencies per a diferents
tipus d'usuaris.
Obrim JCreator i creem un projecte
on anirem deixant tots els fitxers de la pràctica. Creem, en primer
lloc, el nostre Usuari en forma d'interfície;
escrivim a Usuari.java el codi
següent:
|
|
import java.util.Date;
public interface Usuari {
int absencies(Date dia1, Date dia2);
}
|
|
|
|
|
Implementant una interfície
(interface): |
|
|
|
Observeu la senzillesa del que hem fet: hem creat una interfície
pública que es diu Usuari i que
té un sol element, un mètode
que es diu absencies(Date dia1, Date dia2).
No escrivim com es fa el càlcul de les absències perquè
no es problema de la interfície,
cada classe
que la implementi
ho farà de la seva pròpia forma.
A partir d'ara si volem que alguna classe
implementi Usuari
haurà de definir obligatòriament un mètode
absencies(Date dia1, Date dia2) que tingui exactament
la mateixa forma que a la interfície,
sinó, no podrà ni compil·lar-se.
Ara que ja tenim l'usuari
abstracte, farem el segon pas: crearem les formes
concretes de Professor,
Alumne i Pas.
La classe
Professor:
|
|
import java.util.Date;
class Professor implements Usuari {
public int absencies(Date dia1, Date dia2)
{
//
Aquí hi aniria el mecanisme de càlcul propi pels professors
// El resultat que
retornem és fictici
return 25;
}
}
|
|
|
|
|
Observeu que hem dit que la nova
classe Professor
implementa l'interfície Usuari.
Per tal que Java ens deixi compil·lar,
li hem de definir un mètode absencies(Date
dia1, Date dia2). Amb això ens ajustem a l'estandar de la
interfície. D'aquesta forma proposem
també la solució concreta al càlcul d'absències
per a professors. |
|
|
 |
Un dels avantatges
que té JCreator Professional és
que conté un expert d'implementació
d'interfícies similar a l'expert
de creació de classes de la versió Lite;
donada una classe i indicant-li la interfície
que implementarà, el JCreator
Professional ens crea l'estructura de mètodes
obligatoris. En el nostre exemple, aquest expert no tindria molta
utilitat. Imagineu, però, una interfície
que tingui vint mètodes! |
|
|
|
La classe
Alumne: |
|
|
|
import java.util.Date;
class Alumne implements Usuari {
public int absencies(Date dia1, Date dia2)
{
//
Aquí hi va el mecanisme de càlcul propi pels alumnes
// El resultat torna
a ser fictici
return 73;
}
}
|
|
|
|
|
La classe
Pas: |
|
|
|
import java.util.Date;
class Pas implements Usuari {
public int absencies(Date dia1, Date dia2)
{
//
Aquí va el mecanisme de càlcul propi pel PAS
// El resultat torna
a ser fictici
return 34;
}
}
|
|
|
|
|
Ara que ja hem creat les classes
amb cada mètode particular de càlcul
d'absències, farem el tercer pas, la creació d'una classe
que serveix per a retornar les absències de qualsevol tipus d'usuari: |
|
|
|
import java.util.Date;
public class ControlAbsencies {
private Usuari usuari;
public ControlAbsencies(Usuari
usuari) {
this.usuari = usuari;
}
public int getAbsencies(Date
dia1, Date dia2) {
return usuari.absencies(dia1,dia2);
}
}
|
|
|
|
|
Aquí ens trobem amb alguns dels aspectes que demostren la potència
de les interfícies:
- La classe que acabem
de crear retorna el número d'absències, sigui l'usuari
un alumne, un professor o un membre del PAS. Per al programador, tots
són usuaris, només hem de cridar a un sol mètode
getAbsencies() per a qualsevol d'ells. Això
simplifica enormement el disseny del programa.
- Hem encapsulat el nostre
problema: qualsevol programa que escriguem podrà utilitzar aquesta
classe, ControlAbsencies
sense haver-se de preocupar de quins mecanismes de càlcul hi
ha per darrera o si són mètodes idèntics o particulars
per a cada tipus d'usuari.
És més, si en algun moment remodelem les nostres bases
de dades i canviem la manera d'enregistrar les absències
o el mètode de càlcul, no farà falta tocar ni una
sola línia de ControlAbsencies ni de
cap classe que la utilitzi com a eina.
Només tocarem la classe particular
d'usuari. Això facilita moltíssim la feina al programador,
que pot localitzar i circumscriure amb facilitat els fragments de codi
que ha de canviar o mantenir, amb la tranquil·litat que els canvis
no tindran efectes col·laterals en el programa que està
escrivint.
Finalment en comprovem el funcionament amb el programa Informes.java.
|
|
|
|
import java.util.Date;
import java.text.SimpleDateFormat;
import java.text.ParsePosition;
public class Informes {
public static void main(String args[]) {
Alumne alumne = new
Alumne();
Professor professor
= new Professor();
Pas pas = new Pas();
SimpleDateFormat
df = new SimpleDateFormat("dd-MM-yyyy");
ParsePosition pos
= new ParsePosition(0);
Date dia1 = df.parse("01-09-2002",pos);
pos = new ParsePosition(0);
Date dia2 = df.parse("30-06-2002",pos);
System.out.println("L'alumne
ha fet: "+
new ControlAbsencies(alumne).getAbsencies(dia1,
dia2)+
"absències
aquest curs");
System.out.println("El
professor ha fet: "+
new
ControlAbsencies(professor).getAbsencies(dia1, dia2)+
"
absències aquest curs");
System.out.println("El
PAS ha fet: "+
new
ControlAbsencies(pas).getAbsencies(dia1, dia2)+
"
absències aquest curs");
}
}
|
|
|
|
|
Compileu i executeu el programa. Observareu
com la classe ControlAbsencies va aplicant el mecanisme de càlcul
particular de cada tipus d'usuari. |
|
|
|
Els programadors novells, quan comencen
a estudiar objectes, tendeixen a fer dissenys
de classes que utilitzen intensivament
els atributs d'herència, potser
els més intuïtius d'aquest tipus de programació. Però
està demostrat que l'abús de l'herència
complica bastant el disseny i la legibilitat dels programes. D'entrada,
és més pràctic pensar dissenys horitzontals, on unes
classes utilitzen altres classes,
i utilitzar interfícies per agrupar
comportaments i donar poca profunditat als arbres
d'herència. |
|
|
|
Algunes característiques
de les interfícies (interfaces) |
|
|
|
- Una interfície pot extendre
altres interfícies, i no només
una sola. Això seria correcte:
public interface
Constantsutils extends ConstantsPetites,
ConstantsGrans {
public final double ALTURA = 3.15;
public final int RADI = 34;
} |
- Una interfície
no pot extendre classes.
- Una interfície
hereta tots
els mètodes
i constants
de les superinterfícies,
excepte si els sobreescriu
(override).
- Tots els mètodes
declarats en una interfície
són, per defecte, public abstract.
No es permeten altres tipus com private
o protected.
- Com que els camps
(fields)
en una interfície
són automàticament static
i constants,
la interfície
ens anirà bé per a la creació de grups de valors
constants:
public
interface Setmana {
int DILLUNS = 1, DIMARTS=2, DIMECRES=3,
DIJOUS=4, DIVENDRES=5,
DISSABTE=6, DIUMENGE=7;
} |
- Tot i ser constants, els camps
(fields)
de les interfícies
es poden inicialitzar
amb valors no constants, valors que prendran el primer cop que es carregui
una classe
que implementi la interfície:
public interface Atzar {
int enter = (int) (Math.random()*1000);
}
|
|
|
|
|
|
 |
|
|
|
|