c=a+b;
para obtener la fracción c resultado de la suma de a y b.
Definiremos las operaciones en Java de un modo similar al lenguaje C, pero como en Java no existen funciones que no sean miembros de una clase, definiremos las operaciones como funciones estáticas de una clase que denominamos Fracción.
Los miembros dato
Consideremos la clase que describe una fracción que denominaremos Fraccion. Consta de dos miembros ambos enteros, el numerador num, y del denominador den.public class Fraccion { int num; int den; //... }
Las funciones miembro
Además de los constructores definiremos varias funciones miembro que codifican las operaciones que se realizan con fracciones: suma de dos fracciones, diferencia de dos fracciones, producto, cociente, fracción inversa de una dada, y simplificar dos fracciones. Finalmente, redefiniremos la función toString para obtener una representación en forma de texto de una fracción.Los constructores
Definiremos dos constructores, el constructor por defecto, que da al numerdor el valor cero, y al denominador el valor uno, y el constructor explícito.public Fraccion() { num=0; den=1; } public Fraccion(int x, int y) { num=x; den=y; }
Suma de dos fracciones
Se tratará de definir una función denominada sumar, que realice las operación de sumar dos fracciones. Por tanto, la función sumar tendrá dos parámetros que son dos fracciones a y b, y devolverá una fracción, su declaración seráFraccion sumar(Fraccion a, Fraccion b){ //... }Para codificar la función plantearemos el procedimiento de sumar dos fracciones a y b, cuyos numeradores son a.num y b.num, y cuyos denominadores son a.den y b.den, respectivamente. El resultado se guarda en la fracción c. El numerador c.num y el denominador c.den se obtienen del siguiente modo:

La suma de dos fracciones es otra fracción c que tiene por numerador c.num.
c.num=a.num*b.den+b.num*a.den;y por denominador c.den
c.den=a.den*b.den;Una vez efectuada la suma, la función sumar devuelve la fracción c
return c;El código completo de la función sumar es
Fraccion sumar(Fraccion a, Fraccion b){ Fraccion c=new Fraccion(); c.num=a.num*b.den+b.num*a.den; c.den=a.den*b.den; return c; }
Diferencia de dos fracciones
La función restar es semejante a la función sumar y no requiere más explicación.Fraccion restar(Fraccion a, Fraccion b){ Fraccion c=new Fraccion(); c.num=a.num*b.den-b.num*a.den; c.den=a.den*b.den; return c; }
Producto de dos fracciones
Cuando se multiplican dos fracciones a y b, se obtiene otra fracción c cuyo numerador es el producto de los numeradores, y cuyo denominador es el producto de sus denominadores respectivos.
Fraccion multiplicar(Fraccion a, Fraccion b){ Fraccion c=new Fraccion(); c.num=a.num*b.num; c.den=a.den*b.den; return c; }Podemos ahorrarnos la fracción temporal c, y escribir
Fraccion multiplicar(Fraccion a, Fraccion b){ return new Fraccion(a.num*b.num, a.den*b.den); }
Inversa de una fracción
La función inversa, recibe una fracción en su único argumento y devuelve una fracción cuyo numerador es el denominador de la fracción argumento, y cuyo denominador es el numerador de dicha fracción.
public static Fraccion inversa(Fraccion a){ return new Fraccion(a.den, a.num); }
Cociente de dos fracciones
Cuando se dividen dos fracciones a y b, se obtiene otra fracción c cuyo numerador es el producto del numerador de la primera por del denominador de la segunda, y cuyo denominador es el producto del denominador de la primera por el numerador de la segunda.
Fraccion dividir(Fraccion a, Fraccion b){ return new Fraccion(a.num*b.den, a.den*b.num); }La operación división de dos fracciones es equivalente a multiplicar la fracción a por la inversa de b, de este modo aprovechamos el código de la función inversa.
Fraccion dividir(Fraccion a, Fraccion b){ return multiplicar(a, inversa(b)); }
Simplificar una fracción
Para simplificar una fracción primero hay que hallar el máximo común divisor del numerador y del denominador. la función mcd se encarga de esta tarea. Para ello emplea el algoritmo de Euclides, cuyo funcionamiento se muestra en el siguiente ejemplo. Sea u=1260 y v=231,- En la primera iteración, se halla el resto r de dividir el primero u entre el segundo v. Se asigna a u el divisor v, y se asigna a v el resto r.
- En la segunda iteracción, se halla el resto r de dividir u entre v. Se asigna a u el divisor v, y se asigna a v el resto r.
- Se repite el proceso hasta que el resto r sea cero. El máximo común divisor será el último valor de v.
1260=231*5+105el máximo común divisor es 21.
231=105*2+21
105=21*5+0
Definimos en la clase Fraccion una función mcd que calcula y devuelve el máximo común divisor del numerador y del denominador.
int mcd(){ int u=Math.abs(num); int v=Math.abs(den); if(v==0){ return u; } int r; while(v!=0){ r=u%v; u=v; v=r; } return u; }A continuación definimos la función simplificar, de modo que al aplicarlo sobre una fracción, dicha fracción se reduzca a la fracción equivalente más simple. Para ello, se divide numerador y denominador por el máximo común divisor de ambos números, y devuelve la fracción simplificada.
Fraccion simplificar(){ int dividir=mcd(); num/=dividir; den/=dividir; return this; }Aquí tenemos otro ejemplo del uso de la palabra reservada this. Los miembros dato cambian al dividirlos entre el máximo común divisor y la función devuelve el objeto actual, this.
La función miembro toString
Para mostrar una fracción podemos definir una función miembro denominada imprimirpublic void imprimir(){ System.out.println(num+" / "+den); }Un objeto de la clase Fraccion llama a la función miembro imprimir para mostrar en la consola (una ventana DOS) los valores que guardan sus miembros dato, num y den, el numerador y el denominador. La función imprimir así definida no nos servirá cuando la clase Fraccion se emplee en un contexto gráfico. Ahora bien, como vamos a ver a continuación el lenguaje Java nos proporciona una solución a este problema.
Aunque no se define explícitamente, la clase Fraccion deriva de la clase base Object (la estudiaremos en el siguiente capítulo) y redefine la función miembro pública toString, cuya tarea es la de dar una representación en forma de texto de la fracción.
public String toString(){ String texto=num+" / "+den; return texto; }En la definición de toString vemos que el operador + se usa para concatenar strings (el lenguaje Java convierte automáticamente un dato primitivo en su representación textual cuando se concatena con un string).
Para mostar en la consola el numerador y el denominador de una fracción (objeto de la clase Fraccion) a basta escribir
System.out.println(a);Lo que equivale a la llamada explícita
System.out.println(a.toString());Si queremos mostrar la fracción a en un contexto gráfico g de un applet o de un canvas escribimos
g.drawString("fracción: "+a, 20, 30);donde 20, 30 son las coordenadoas de la línea base del primer carácter. Esta sentencia equivale a la llamada explícita
g.drawString("fracción: "+a.toString(), 20, 30);La redefinición de la función toString devuelve un string un objeto de la clase String que guarda la representación en forma de texto de los objetos de una determinada clase. De este modo, una clase que redefina toString puede emplearse en cualquier ámbito.
La clase Fraccion
Ahora ponemos las funciones miembro dentro de la clase Fraccion, anteponiendo en las funciones que representan operaciones la palabra reservada static.public class Fraccion { private int num; private int den; public Fraccion() { num=0; den=1; } public Fraccion(int x, int y) { num=x; den=y; } public static Fraccion sumar(Fraccion a, Fraccion b){ Fraccion c=new Fraccion(); c.num=a.num*b.den+b.num*a.den; c.den=a.den*b.den; return c; } public static Fraccion restar(Fraccion a, Fraccion b){ Fraccion c=new Fraccion(); c.num=a.num*b.den-b.num*a.den; c.den=a.den*b.den; return c; } public static Fraccion multiplicar(Fraccion a, Fraccion b){ return new Fraccion(a.num*b.num, a.den*b.den); } public static Fraccion inversa(Fraccion a){ return new Fraccion(a.den, a.num); } public static Fraccion dividir(Fraccion a, Fraccion b){ return multiplicar(a, inversa(b)); } private int mcd(){ int u=Math.abs(num); int v=Math.abs(den); if(v==0){ return u; } int r; while(v!=0){ r=u%v; u=v; v=r; } return u; } public Fraccion simplificar(){ int dividir=mcd(); num/=dividir; den/=dividir; return this; } public String toString(){ String texto=num+" / "+den; return texto; } } |
Uso de la clase Fraccion
Como vemos en la definición de la clase Fraccion tenemos funciones estáticas y no estáticas. Vamos a ver la diferencia entre las llamadas a funciones estáticas y no estáticas.- Crear un objeto de la clase Fraccion o una fracción
Fraccion x=new Fraccion(2,3);
- Mostrar una fracción
System.out.println("x--> "+x);
Cuando se pone una fracción x como argumento de la función println o se concatena con un string se llama automáticamente a la función miembro toString, lo que equivale a la siguiente llamada
System.out.println("x--> "+x.toString());
- Suma de dos fracciones
Fraccion x=new Fraccion(2,3); Fraccion y=new Fraccion(4,3); System.out.println("x+y= "+Fraccion.sumar(x, y));
- Producto de dos fracciones
Fraccion x=new Fraccion(2,3); Fraccion y=new Fraccion(4,3); System.out.println("x*y= "+Fraccion.multiplicar(x, y));
- Operaciones combinadas
Primero suma las fracicones x e y y luego hace el producto con la fracción z
Fraccion x=new Fraccion(2,3); Fraccion y=new Fraccion(4,3); Fraccion z=new Fraccion(1,2); Fraccion resultado=Fraccion.multiplicar(Fraccion.sumar(x,y),z); System.out.println("(x+y)*z= "+resultado);
- Simplificar una fracción
System.out.println(resultado.simplificar());
Modificadores de acceso
Este ejemplo ilustra una faceta importante de los lenguajes de Programación Orientada a Objetos denominada encapsulación. El acceso a los miembros de una clase está controlado. Para usar una clase, solamente necesitamos saber que funciones miembro se pueden llamar y a qué datos podemos acceder, no necesitamos saber como está hecha la clase, como son sus detalles internos. Una vez que la clase está depurada y probada, la clase es como una caja negra. Los objetos de dicha clase guardan unos datos, y están caracterizados por una determinada conducta. Este ocultamiento de la información niega a la entidades exteriores el acceso a los miembros privados de un objeto. De este modo, las entidades exteriores acceden a los datos de una manera controlada a través de algunas funciones miembro. Para acceder a un miembro público (dato o función) basta escribir.objeto_de_la_clase_Fraccion.miembro_público_no_estático clase_Fraccion.miembro_público_estáticoDelante de los miembros dato, como podemos ver en el listado hemos puesto las plabras reservadas public y private.
- Miembros públicos
- Miembros privados
Los miembros dato num y den son privados, y también la función que calcula el máximo común divisor mcd, que es una función auxiliar de la función miembro publica simplificar. El usuario solamente precisa saber que dispone de una función pública que le permite simplificar una fracción, pero no necesita saber cuál es el procedimiento empleado para simplificar fracciones. Así declaramos la función mcdcomo privada y simplificar como pública.
- Por defecto (a nivel de paquete)
package es la primera sentencia que se pone en un archivo .java. El nombre del paquete es el mismo que el nombre del subdirectorio que contiene los archivos .java. Cada archivo .java contiene habitualmente una clase. Si tiene más de una solamente una de ellas es pública. El nombre de dicha clase coincide con el nombre del archivo.
Como el lector se habrá dado cuenta hay una correspondencia entre archivos y clases, entre paquetes y subdirectorios. El Entorno Integrado de Desarrollo (IDE) en el que creamos los programas facilita esta tarea sin que el usuario se aperciba de ello.
Mejora de la clase Lista
Veamos un ejemplo más, la clase Lista, y hagamos uso de la función miembro ordenar para hallar el valorMenor y el valorMayor. Si tenemos una lista ordenada en orden creciente, el valor menor es el primer elemento de la lista x[0], y el valor mayor es el último elemento de la lista x[n-1]. Podemos escribir el siguiente código
public int valorMayor(){ ordenar(); return x[n-1]; } public int valorMenor(){ ordenar(); return x[0]; }Podemos llamar una sóla vez a la función miembro ordenar en el constructor, después de haber creado el array, y evitar así la reiteración de llamadas a dicha función en valorMayor, valorMenor eimprimir.
public Lista(int[] x) { this.x=x; n=x.length; ordenar(); } public int valorMayor(){ return x[n-1]; } public int valorMenor(){ return x[0]; }La función miembro ordenar, es una función auxiliar de las otras funciones miembro públicas, por tanto, podemos ponerle delante el modificador de acceso private. El usuario solamente está interesado en el valor medio, el valor mayor y menor de un conjunto de datos, pero no está interesado en el procedimiento que permite ordenar el conjunto de datos. Como ocurre en la vida moderna usamos muchos aparatos pero no tenemos por que conocer sus detalles internos y cómo funcionan por dentro. Una clase es como uno de estos aparatos modernos, el usuario solamente tiene que conocer qué hace la clase, a qué miembros tiene acceso, pero no como está implementada en software.
La función miembro toString
Si los miembros dato de la clase Lista son privados (private) hemos de definir una función que hemos denominado imprimir para mostrar los valores que guardan los miembros dato de los objetos de la clase Lista.public class Lista { private int[] x; //array de datos private int n; //dimensión //... public void imprimir(){ for(int i=0; i<n; i++){ System.out.print("\t"+x[i]); } System.out.println(""); }La llamada a esta función miembro se efectúa desde un objeto de la clase Lista
Lista lista=new Lista(new int[]{60, -4, 23, 12, -16}); System.out.println("Mostrar la lista"); lista.imprimir();Sustituímos la función miembro imprimir por la redefinición de toString. Para redefinir una función, tiene que tener el mismo nombre, los mismos modificadores, el mismo tipo de retorno y los mismos parámetros y del mismo tipo en la clase base y en la clase derivada. Para evitar errores, el mejor procedimiento es el de ir al código de la clase base Object, copiar la línea de la declaración de toString, pegarla en la definición de nuestra clase, y a continuación definir dicha función.
public class Lista { private int[] x; //array de datos private int n; //dimensión //... public String toString(){ String texto=""; for(int i=0; i<n; i++){ texto+="\t"+x[i]; } return texto; }La llamada a la función toString se realiza implícitamente en el argumento de la función System.out.println, o bien, al concatenar un string y un objeto de la clase Lista.
Lista lista=new Lista(new int[]{60, -4, 23, 12, -16}); System.out.println("Mostrar la lista"); System.out.println(lista);
public class Lista { private int[] x; //array de datos private int n; //dimensión public Lista(int[] x) { this.x=x; n=x.length; ordenar(); } public double valorMedio(){ int suma=0; for(int i=0; i<n; i++){ suma+=x[i]; } return (double)suma/n; } public int valorMayor(){ return x[n-1]; } public int valorMenor(){ return x[0]; } private void ordenar(){ int aux; for(int i=0; i<n-1; i++){ for(int j=i+1; j<n; j++){ if(x[i]>x[j]){ aux=x[j]; x[j]=x[i]; x[i]=aux; } } } } public String toString(){ String texto=""; for(int i=0; i<n; i++){ texto+="\t"+x[i]; } return texto; } } |
No hay comentarios:
Publicar un comentario