Mòdul 2

Pràctica 6: Aritmètica i fraccions (II)  
Tornar presentació tema
Pràctica 1 Pràctica 2 Pràctica 3 Pràctica 4 Pràctica 5 Pràctica 6  
     
 

Ara, amb els mètodes de la classe NombresEnters de la pràctica anterior, ja tens els materials necessaris per escriure la classe central del projecte Aritmetica: la classe Fraccio.java.

 
     
  La classe central del projecte: Fraccio.java  
     
  Des del punt de vista de l'aritmètica elemental, una fracció és un objecte que conté dos nombres enters: el numerador i el denominador. La classe Fraccio ha de tenir, per tant, dues variables d'instància que es corresponguin amb aquest fet:  
     

/**
* La classe Fraccio representa una fraccio de nombres enters.
*
* @author Carles Romero
* @version 2004/02/14
*/

public class Fraccio {

    /**
     * El numerador d'aquesta Fraccio.
     */

    private int numerador;

    /**
     * El denominador d'aquesta Fraccio.
     */

    private int denominador;

}

 
     
  Ara el mètode constructor pels objectes de la classe Fraccio:  
     

 

 

 

 

 

 

 

 

/**
* La classe Fraccio representa una fraccio de nombres enters.
*
* @author Carles Romero
* @version 2004/02/14
*/
public class Fraccio {     

     /**
     * El numerador d'aquesta Fraccio.
     */
    private int numerador;

    /**
     * El denominador d'aquesta Fraccio.
     */
    private int denominador;

    /**
     * Mètode constructor per a objectes de la classe Fraccio.
     * @param elNumerador el numerador d'aquesta Fraccio
     * @param elDenominador el denominador d'aquesta Fraccio
     */

    public Fraccio (int elNumerador,int elDenominador)     {  //constructor
        numerador=elNumerador;
        denominador=elDenominador;
    }
}
 
     
  el qual, de moment, senzillament posa els valors del numerador i del denominador al seu lloc.  
     
  Resulta convenient, però, que la fracció estigui simplificada al màxim i que el denominador sigui positiu. Això es pot aconseguir fàcilment: per simplificar la fracció només cal dividir numerador i denominador pel seu màxim comú divisor i, si el denominador és negatiu, només cal canviar el signe de numerador i denominador. Cal, doncs, un mètode que faci això i cridar-lo des del mètode constructor. Fixa't bé en els fragments de codi que cal afegir.  
     

 

 

 

 

 

 

 

 

 

 


/**
* La classe Fraccio representa una fraccio de nombres enters.
*
* @author Carles Romero
* @version 2004/02/14
*/
public class Fraccio {     /**
     * El numerador d'aquesta Fraccio.
     */
    private int numerador;

    /**
     * El denominador d'aquesta Fraccio.
     */
    private int denominador;

    /**
     * Mètode constructor per a objectes de la classe Fraccio.
     * @param elNumerador el numerador d'aquesta Fraccio
     * @param elDenominador el denominador d'aquesta Fraccio
     */
    public Fraccio (int elNumerador,int elDenominador)     {    //constructor
        numerador=elNumerador;
        denominador=elDenominador;

        normalitza();
    }
    void normalitza () {
        int d=NombresEnters.Mcd(numerador,denominador);
        numerador=numerador/d;
        denominador=denominador/d;
            if (denominador<0) {
                denominador=-denominador;
                numerador=-numerador;
            }
    }
}
 
     
  Ara hi ha dues observacions a fer:  
     
  • Les dues variables d'instància, numerador i denominador, han estat definides com a private. La clàusula private aplicada a una variable d'instància la fa completament invisible des de fora de la classe: cap altra classe, cap altre mètode que no sigui de la pròpia classe pot veure'n el valor i, molt menys, canviar-lo. Aquestes variables han estat encapsulades dintre de la classe. L'encapsulació és una de les dues característiques fonamentals dels llenguatges de programació orientats a objectes, com ho és Java (l'altra característica és l'herència). Les variables que no interessi encapsular es declaren com a public (però aquesta és una pràctica molt poc recomanable! No és convenient que tothom pugui manipular les nostres variables!)

  • Recorda com hem explicat a la pràctica anterior que es crida un mètode public static d'una altra classe. Això es fa tot posant el nom de la classe com a prefix del nom del mètode, ambdós elements separats per un punt.
            La_meva_classe.el_meu_metode(<... paràmetres...>);

    En aquest cas a la línia
            int d=NombresEnters.Mcd(numerador,denominador);
    es demana el mètode public static int Mcd (int a,int b) de la classe NombresEnters.
 
     
  Però, però... com podrem demanar els valors del numerador i del denominador de la fracció si són variables private? Res més senzill: un parell de mètodes getter fan la feina:  
     
    /**
     * Per obtenir el numerador d'aquesta Fraccio.
     * @return el numerador d'aquesta Fraccio
     */

    public int getNumerador () {
        return numerador;
    }

    /**
     * Per obtenir el denominador d'aquesta Fraccio.
     * @return el denominador d'aquesta Fraccio
     */

    public int getDenominador () {
        return denominador;
    }
 
     
  Un altre mètode constructor    
     
  Un nombre enter es pot considerar com una fracció amb denominador 1: Per exemple, 5 = 5/1 i -12 = -12/1. Això ho pots recollir amb un nou mètode constructor per a la classe Fraccio:  
     
    /**
     * Mètode constructor per a objectes de la classe Fraccio.
     * @param unNombreEnter que és el numerador d'aquesta Fraccio.
     * El denominador serà 1
     */

    public Fraccio (int unNombreEnter) { // constructor
        numerador=unNombreEnter;
        denominador=1;
    }
 
     
  Com és obvi, no cal normalitzar, oi?  
     
Observa que els dos constructors tenen el mateix nom: Fraccio. Com s'ho fa la Màquina Virtual de Java per saber quin dels dos constructors es demana en cada cas?  
 
  • La resposta és que la Màquina Virtual de Java sap fer-ne la distinció a partir del nombre i la mena dels paràmetres que cadascun dels mètodes demana. Aquest fet, que la Màquina Virtual de Java pugui distingir entre diversos mètodes amb el mateix nom, però diferents quant al nombre i/o mena dels seus paràmetres, se sol indicar tot dient que Java admet sobrecàrrega de mètodes.
 
     
  Dues fraccions especials    
     
  D'entre totes les fraccions que hi ha, n'hi ha dues de ben especials: 0 = 0/1 i 1 = 1/1. Les pots incloure a la classe com a camps public static:  
     

 

 

 

 

 

 

 

 


/**
* La classe Fraccio representa una fraccio de nombres enters.
*
* @author Carles Romero
* @version 2004/02/14
*/
public class Fraccio {

    /**
     * El numerador d'aquesta Fraccio.
     */
    private int numerador;

    /**
     * El denominador d'aquesta Fraccio.
     */
    private int denominador;

}

    /**
     * La Fraccio 0.
     */

    public static Fraccio FRACCIO_0=new Fraccio(0);

    /**
     * La Fraccio 1.
     */

    public static Fraccio FRACCIO_1=new Fraccio(1);
 
     
Observa el nom d'aquests camps: FRACCIO_0 i FRACCIO_1. És una pràctica comú anomenar els camps public static amb lletres majúscules: tots els programadors (bé, quasi tots) ho fan!  
     
  Per poder veure les fraccions de la manera "normal"    
     
  Potser convindrà, en algun moment, poder "veure" les fraccions escrites aixi: 3/7. El següent mètode transforma la fraccio Fraccio en una cadena de caràcters:  
     

    /**
     * Conversió d'aquesta Fraccio a cadena de caràcters.
     * @return una cadena en la forma numerador/denominador
    */

    public String aCadena () {
            if (esUnNombreEnter()) {
                return String.valueOf(numerador);
            } else {
                return String.valueOf(numerador)+
                       "/"+String.valueOf(denominador);
            }
    }

 
     
  Aquest mètode depèn, però, d'altres dos mètodes:  
     
 
  • La línia
                if (esUnNombreEnter()) {
    pregunta si la fracció és un nombre enter, per tal que, si ho és, la cadena no contingui ni la barra "/" ni el denominador (que és 1).

    Aquest mètode cal escriure'l i és aquest:
 

    /**
     * Comprova si aquesta Fraccio és un nombre enter.
     * @return true si aquesta Fraccio és un nombre enter
     */

    public boolean esUnNombreEnter () {
        return (denominador==1);
    }

 
     
 
  • La sentència String.valueOf(numerador) crida al mètode
                      public static String valueOf(int n)

    de la classe del SDK de Java, java.lang.String.

    Aquest mètode es crida, tal com s'explica més amunt, posant el nom de la classe (String) com a prefix del nom del mètode (valueOf), ambdós elements separats per un punt. Aquest mètode converteix en nombre enter int 5 en la cadena String "5", el nombre enter int -127 en la cadena de text String "-127", etc.
 
  Vegem què hem fet fins ara  
     
  Compila tot el projecte i crea la fracció 12/21:  
     
 
     
 
 
     
  i comença per inspeccionar-la:  
     
 
     
 
 
     
  Molt bé! és clar que 12/21 = 4/7... El mètode normalitza() ha funcionat a la perfecció!  
     
  Ara la pots veure com a cadena de caràcters:  
     
 
     
 
 
     
  o demanar-ne el denominador:  
     
 
     
 
 
     
  Prova-ho ara amb un nombre enter, 7, per exemple:  
     
 
     
 
 
     
  La inspecciones:  
     
 
     
 
 
     
  i, és clar, 7 = 7/1. Amb el mètode public String aCadena(), pots veure'n l'aspecte com a cadena de caràcters de text:  
     
 
     
  Tot completant la classe amb els mètodes que falten  
     
  Ara ja només falta afegir els mètodes que permetin fer totes les operacions aritmètiques amb fraccions.  
     
 
  • Tres mètodes per esbrinar el signe de la fracció:
 
    /**
     * Comprova si aquesta Fraccio és positiva.
     * @return true si aquesta Fraccio és positiva
     */

    public boolean esPositiva () {
        return (numerador>0);
    }

    /**
     * Comprova si aquesta Fraccio és zero.
     * @return true si aquesta Fraccio és zero
     */

    public boolean esZero () {
        return (numerador==0);
    }

    /**
     * Comprova si aquesta Fraccio és negativa.
     * @return true si aquesta Fraccio és negativa
     */

    public boolean esNegativa () {
        return (numerador<0);
    }
 
     
 
  • Cinc mètodes per comparar la fracció Fraccio amb d'altres fraccions. Dos d'ells depenen del mètode public Fraccio restaLi (Fraccio f) que vé després.
 
    /**
     * Comprova si aquesta Fraccio és equivalent a una altra
     * Fraccio.
     * @param f una altra Fraccio
     * @return true si aquesta Fraccio és equivalent a aquesta
     * altra Fraccio
     */

    public boolean esEquivalentA (Fraccio f) {
        return (numerador*f.getDenominador()==
                denominador*f.getNumerador());
    }

    /**
     * Comprova si aquesta Fraccio és més petita que una altra
     * Fraccio.
     * @param f una altra Fraccio
     * @return true si aquesta Fraccio és més petita que aquesta
     * altra Fraccio
     */

    public boolean esMesPetitaQue (Fraccio f) {
        return restaLi(f).esNegativa();
    }

    /**
     * Comprova si aquesta Fraccio és més gran que una altra
     * Fraccio.
     * @param f una altra Fraccio
     * @return true si aquesta Fraccio és més gran que aquesta
     * altra Fraccio
     */

    public boolean esMesGranQue (Fraccio f) {
        return restaLi(f).esPositiva();
    }

    /**
     * Comprova si aquesta Fraccio és més petita o igual que
     * una altra Fraccio.
     * @param f una altra Fraccio
     * @return true si aquesta Fraccio és més petita o igual
     * que aquesta altra Fraccio
     */

    public boolean esMesPetitaOIgualQue (Fraccio f) {
        return !esMesGranQue(f);
    }

    /**
     * Comprova si aquesta Fraccio és més gran o igual que una
     * altra Fraccio.
     * @param f una altra Fraccio
     * @return true si aquesta Fraccio és més gran o igual que
     * aquesta altra Fraccio
     */

    public boolean esMesGranOIgualQue (Fraccio f) {
        return !esMesPetitaQue(f);
    }

 
     
 
  • Tres operacions que impliquen un sol operand: canvi de signe, inversió (pas a la fracció inversa o recíproca) i valor absolut:
 
    /**
     * Canvi de signe.
     * @return una Fraccio que és la oposada d'aquesta Fraccio
     */

    public Fraccio canviaSigne () {
        return new Fraccio(-numerador,denominador);
    }

    /**
     * Inversió.
     * @return una Fraccio que és la inversa d'aquesta Fraccio
     */

    public Fraccio inverteix () {
        return new Fraccio(denominador,numerador);
    }

    /**
     * Valor absolut.
     * @return una Fraccio que és el valor absolut d'aquesta
     * Fraccio
     */

    public Fraccio valorAbsolut () {
        return new Fraccio
            (NombresEnters.valorAbsolut(numerador),
                           denominador);
    }
 
     
 
  • Suma, resta, multiplicació i divisió:
 
    /**
     * Suma una altra Fraccio a aquesta Fraccio.
     * @param f una altra Fraccio
     * @return una Fraccio que es la suma d'aquesta Fraccio
     * amb l'altra Fraccio
     */

    public Fraccio sumaLi (Fraccio f) {
        int den=NombresEnters.Mcm(denominador,
                                  f.getDenominador());
        int num=numerador*den/denominador+
                f.getNumerador()*den/f.getDenominador();
        return new Fraccio(num,den);
    }

    /**
     * Resta una altra Fraccio d'aquesta Fraccio.
     * @param f una altra Fraccio
     * @return una Fraccio que es la resta d'aquesta Fraccio
     * menys l'altra Fraccio
     */

    public Fraccio restaLi (Fraccio f) {
        int den=NombresEnters.Mcm(denominador,
                                  f.getDenominador());
        int num=numerador*den/denominador-
                f.getNumerador()*den/f.getDenominador();
        return new Fraccio(num,den);
    }

    /**
     * Multiplicació d'aquesta Fraccio per una altra Fraccio.
     * @param f una altra Fraccio
     * @return una Fraccio que es el producte d'aquesta Fraccio
     * per l'altra Fraccio
     */

    public Fraccio multiplicaPer (Fraccio f) {
        return new Fraccio(numerador*f.getNumerador(),
                           denominador*f.getDenominador());
    }

    /**
     * Divisió d'aquesta Fraccio entre una altra Fraccio.
     * @param f una altra Fraccio
     * @return una Fraccio que es la divisió d'aquesta Fraccio
     * entre l'altra Fraccio
     */

    public Fraccio divideixEntre (Fraccio f) {
        return multiplicaPer(f.inverteix());
    }
 
     
  • I, per acabar, la potència entera de la fracció Fraccio, escrita com a mètode recursiu:
 
      /**
     * Potència entera d'aquesta Fraccio.
     * @param n l'exponent enter
     * @return una Fraccio que és aquesta Fraccio elevada
     * a la potència n
     */

    public Fraccio potencia (int n) {
            if (n<0) {
                return potencia(-n).inverteix();
            } else if (n==0) {
                return FRACCIO_1;
            } else {
                return multiplicaPer(potencia(n-1));
            }
    }
 
     
Atenció!!!  
     
 

/*
L'estructura

            if (<una_condició>) {
                 <... fes això ...>
            } else if (<una_altra_condició>) {
                 <... fes això altre ...>
            } else {
                 <... fes allò de més enllà ...>
            }

és completament equivalent a

            if (<una_condició>) {
                 <... fes això ...>
            } else {
                    if (<una_altra_condicio>) {
                        <... fes això altre ...>
                    } else {
                        <... fes allò de més enllà ...>
                    }
            }

i es pot repetir tantes vegades com calgui.
*/

 
     
  Ja està: ja tens totes les operacions amb fraccions disponibles!  
     
Cal que tinguis en compte que algunes de les operacions que tens definides en els mètodes són impossibles si el codi intenta dividir per 0. La Màquina Virtual de Java ho adverteix, llença un objecte Exception i interromp l'execució del codi. Per exemple, només que vulguis construir una fracció amb numerador i denominador iguals a zero, ho pots comprovar:  
     
 
     
 
 
     
 
 
     
  En mòduls posteriors t'explicarem com es pot fer una bona gestió de les excepcions, per tal d'eliminar la possibilitat que l'execució del codi s'interrompi de manera imprevista.  
     
Un exercici  
     
  Es tracta d'afegir una nova classe al projecte, EquacioPrimerGrau, la qual sigui capaç de resoldre l'equació:  
     
 
 
     
  per a fraccions p/q i r/s donades.  
     
  L'esquelet de la classe ha de ser aquest:  
     
 
/**
 * Classe que resol equacions de primer grau amb coeficients
 * fraccionaris.
 *
 * @author (el vostre nom)
 * @version (un número de versió o la data)
 */

public class EquacioPrimerGrau {

    /**
     * El coeficient de la incògnita x.
     */

    private Fraccio coeficientX;

    /**
     * El terme independent.
     */

    private Fraccio termeIndependent;

    /**
     * Mètode constructor per a objectes de la classe
     * EquacioPrimerGrau.
     * @param p el numerador del coeficient de la incògnita x
     * @param q el denominador del coeficient de la incògnita x
     * @param r el numerador del terme independent
     * @param s el denominador del terme independent
     */

    public EquacioPrimerGrau (int p,int q,int r,int s) {
// constr.
        
< posa el codi que falta aquí... >
    }

  &n