Repaso: Uso de objetos como records
Cuando se desea usar los objetos como records, las clases que definen estos objetos no llevan métodos. Sólo se declaran las variables de instancia que se incluyen en cada objeto, es decir cada record.
Cuando se tiene una referencia de un record, sus variables de instancia se leen y modifican directamente. Es decir que el programa que usa los records usa directamente la representación de los records.
Uso de objetos como tipos de datos abstractos
Cuando los programas son grandes, es recomendable usar los objetos como tipos de datos abstractos. Un tipo de datos abstracto (T.D.A.) se manipula a través de un conjunto bien definido de operaciones. Por ejemplo, Ud. ha usado las clases Map, TextReader, Tortuga, etc. por medio de sus operaciones sin tener idea de qué variables de instancia se utilizan para representar esos objetos y como están programados (implementados) sus métodos.
Por eso se habla de abstracción: el programa que usa los objetos como tipos de datos abstractos, se abstrae de su implementación.
Los tipos de datos primitivos en Java como int, double y boolean también son tipos de datos abstractos, porque Ud. no necesita saber como se representan para trabajar con ellos, ni saber como se implementan la suma, multiplicación, división en los circuitos del computador.
en donde la clase Tiempo ha sido definida al final del archivo como:
Tiempo t1= new Tiempo();
print("Horas 1 ? ");
t1.horas= readInt();
print("Minutos 1 ? ");
t1.min= readInt();
Tiempo t2= new Tiempo();
print("Horas 2 ? ");
t2.horas= readInt();
print("Minutos 2 ? ");
t2.min= readInt();
t1.horas= t1.horas+t2.horas+(t1.min+t2.min)/60;
t1.min= (t1.min+t2.min)%60;
print("Suma= "+t1.horas+":"+t1.min);
La desventaja de esta forma de programar (sin abstracción) es que si
deseamos cambiar la representación del tiempo por una representación que
sólo contenga minutos, hay que cambiar todos los lugares del programa
en donde se haya usado la clase Tiempo.
class Tiempo {
int horas;
int min;
}
Dicho de otra forma, descompondremos el problema inicial en (i) un programa que construye el arreglo de objetos, lo ordena y luego lo escribe en otro archivo, y (ii) la clase Post con operaciones sobre los postulantes.
Cuando se define una clase, se debe decidir qué operaciones se necesitan para esa clase. Inspeccionando el programa original, nos damos cuenta que se necesita leer un postulante de un archivo, compararlo en el orden lexicográfico con otro postulante, desplegar sus datos en pantalla y escribirlo en disco.
Con esas operaciones podemos reescribir el programa que lee el archivo y lo ordena por nombres, de modo que este programa nunca manipule directamente las variables de instancia de un objeto de la clase Post.
y el método ordenar quedaría prácticamente igual. Sólo cambia
la comparación entre nombres de postulantes porque en la solución
original se accesan sus variables de instancia:
// Leer de "post.dat"
int npost= ...; // el numero de postulantes en el archivo
Post[] posts= new Post[npost];
TextReader lect= new TextReader("post.dat");
int i= 0;
while (true) {
String lin= lect.readLine();
if (lect.eofReached())
break;
posts[i]= new Post(lin);
i= i+1;
}
// Ordenar
ordenar(posts, npost);
// Escribir en "post2.dat"
TextWriter escr= new TextWriter("post2.dat");
i= 0;
while (i<npost) {
posts[i].escribir(escr);
i= i+1;
}
Una vez determinados cuáles son los métodos que necesita la
clase Post, volvemos a definirla:
void ordenar(Post[] a, int n) {
...
if (a[i].compararCon(a[i+1])>0) { ... }
...
}
Observe que al implementar las operaciones de la clase Post, se hace
indispensable accesar sus variables de instancia. Esa es la idea
de la abstracción: postergar al máximo el acceso a las variables
de instancia. Lo importante es que los programas que usan
la clase Post no accesen directamente las variables de instancia.
class Post extends Program {
String ci;
String nombre;
String ptje;
Post(String lin) {
FieldParser decod= new FieldParser(lin, ":");
this.ci= decod.readString();
this.nombre= decod.readString();
this.ptje= decod.readInt();
}
void escribir(TextWriter escr) {
escr.println(this.ci+":"+this.nombre+":"+this.ptje);
}
int compararCon(Post post) {
return compare(this.nombre, post.nombre);
}
}
Ejercicio 1:
El archivo "post.dat" ahora tiene como cuarto campo la dirección del postulante. Modifique la clase Post de modo que ahora se tenga en cuenta la presencia de la dirección.
Este ejercicio permite apreciar que no es necesario cambiar una coma del programa que usa la clase Post. El programador se concentra sólo en la definición de la clase Post.
Ejercicio 2:
Ahora se necesita que el archivo "post2.dat" quede ordenado por puntaje descendentemente y no ascendentemente por nombre. Sólo modifique la clase Post para tener en cuenta este nuevo requerimiento (no puede modificar el programa que usa la clase Post).
Este ejercicio permite apreciar nuevamente que no es necesario alterar el programa que usa la clase.
Ejercicio 3:
El número de postulantes ha aumentado considerablemente haciendo que el algoritmo de ordenamiento sea muy lento. Se requiere programar un nuevo método de ordenamiento más eficiente. Modifique el procedimiento ordenar para que utilice el algoritmo quicksort (para esto introduzca un nuevo procedimiento que ordene un rango de índices en el arreglo).
El ejercicio permite apreciar que podemos cambiar el algoritmo de ordenamiento sin modificar la lectura de los postulantes, su escritura o la clase Post.
Conclusión:
El beneficio de la abstracción es doble: Primero permite disminuir la complejidad de los problemas ignorando los detalles de la resolución del problema completo. Y segundo, permite cambiar componentes de la solución para adaptarse a nuevos requerimientos del problema.
La desventaja de la abstracción es que no es fácil darse cuenta en donde hay que establecer los límites de cada subproblema de modo que se minimicen las operaciones que se necesitarán.
Observe que en (*) se necesita accesar el nombre de un objeto que no
es this. En este caso es necesario entonces colocar el prefijo
post. para diferenciarlo del acceso a las variables de this.
class Post extends Program {
String ci;
String nombre;
String ptje;
Post(String lin) {
FieldParser decod= new FieldParser(lin, ":");
ci= decod.readString();
nombre= decod.readString();
this.ptje= decod.readInt();
}
void escribir(TextWriter escr) {
escr.println(ci+":"+nombre+":"+this.ptje);
}
int compararCon(Post post) {
return compare(nombre, post.nombre); // (*)
}
}