Esercizio interfacce Measurable e Measurer

Forum di discussione sul linguaggio Java e JSP

Moderatore: Toki

Regole del forum
Leggi: IProgrammatori.it - Regolamento Forum
mvinci
New Entry
Messaggi: 5
Iscritto il: 11 ago 2017, 10:10

Esercizio interfacce Measurable e Measurer

Messaggioda mvinci » 11 ago 2017, 11:24

Salve, ho bisogno di consigli per risolvere questo esercizio in Java, la traccia è questa:

Migliorate la classe DataSet in modo che possa essere usata con un oggetto di tipo Measurer oppure per elaborare oggetti di tipo Measurable.
Suggerimento: fornite un costruttore senza parametri che crei un oggetto di tipo Measurer, il quale elabori oggetti di tipo Measurable.

Le interfacce sono queste:

Codice: Seleziona tutto

public interface Measurable
{
    double getMeasure();
}

---------------------------------------------------------

Codice: Seleziona tutto

/**
 * Descrive una qualsiasi classe i cui esemplari possano misurare altri oggetti.
 */
public interface Measurer
{
    /**
     * Calcola la misura di un oggetto
     * @param anObject l'oggetto da misurare
     * @return la misura
     */
    double measure(Object anObject);
}

------------------------------------------------------

La Classe DataSet

Codice: Seleziona tutto

/**
 * Calcola la media di un insieme di valori.
 */
public class DataSet
{
    /**
     * Costruiesce un insieme vuoto di dati con un misuratore assegnato.
     * @param aMeasurer il misuratore che viene usato per misurare i valori dei dati
     */
    public DataSet(Measurer aMeasurer)
    {
        sum = 0;
        count = 0;
        maximum = null;
        measurer = aMeasurer;
    }
   
    /**
     * Aggiunge un valore all'insieme di dati.
     * @param x il valore da aggiungere
     */
    public void add(Object x)
    {
        sum = sum + measurer.measure(x);
       
        if (count == 0 || measurer.measure(maximum) < measurer.measure(x))      maximum = x;
        count++;
    }
   
    /**
     * Restituisce la media dei dati inseriti.
     * @return la media oppure 0 se non sono stati inseriti valori.
     */
    public double getAverage()
    {
        if (count == 0) return 0;
        else return sum / count;
    }
   
    /**
     * Restituisce il dato maggiore tra i dati inseriti.
     * @return il dato maggiore o null se non sono stati inseriti valori
     */
    public Object getMaximum()
    {
        return maximum;
    }
   
    private double sum;
    private int count;
    private Object maximum;
    private Measurer measurer;
}

-------------------------------------------------------------

Ciò che segue è un esempio di come funziona la classe DataSet originale aggiungendo la classe RectangleMeasurer e la classe DataSetTester2

Codice: Seleziona tutto

import java.awt.Rectangle;

/**
 * Gli oggetti di questa classe misurano rettangoli in base alla loro area.
 */
public class RectangleMeasurer implements Measurer
{
    public double measure(Object anObject)
    {
        Rectangle aRectangle = (Rectangle) anObject;
        double area = aRectangle.getWidth() * aRectangle.getHeight();
        return area;
    }
}


-----------------------------------------------------------------------------

Codice: Seleziona tutto

import java.awt.Rectangle;
/**
 * Illustra l'utilizzo di un oggetto di tipo Measurer.
 */
public class DataSetTester2
{
    public static void main(String[] args)
    {
        Measurer m = new RectangleMeasurer();
       
        DataSet data = new DataSet(m);
       
        data.add(new Rectangle(5, 10, 20, 30));
        data.add(new Rectangle(10, 20, 30, 40));
        data.add(new Rectangle(20, 30, 5, 10));
       
        System.out.println("Average area = " + data.getAverage());
       
        Rectangle max = (Rectangle) data.getMaximum();
        System.out.println("Maximum area rectangle = " + max);
    }
}


--------------------------------------------------------
Output di DataSetTester2

Average area = 616.6666666666666
Maximum area rectangle = java.awt.Rectangle[x=10,y=20,width=30,height=40]
---------------------------------------------------------

Ora dovrei come da suggerimento scrivere un costruttore senza parametri

Codice: Seleziona tutto

public DataSet()
{
   sum = 0;
   count = 0;
   maximum = null;
   
}


però non so come continuare: il suggerimento dice di creare un oggetto di tipo Measurer che elabori oggetti di tipo Measurable ma non so come.
xneo
Utente Semi-Senior
Messaggi: 187
Iscritto il: 12 mar 2013, 14:02

Re: Esercizio interfacce Measurable e Measurer

Messaggioda xneo » 12 ago 2017, 10:43

Secondo me manca qualcosa. Non è che per caso DataSet dovrebbe essere generico in un tipo T sottotipo Measurable?
Cioè

Codice: Seleziona tutto

public class DataSet<T extends Measurable> {...}

Così come Measurer, l'avrei scritto come Measurer<T>, di conseguenza il metodo sarebbe stato measure(T measuree)

In questo modo potresti creare questo costruttore

Codice: Seleziona tutto

private Measurer<T> measurer;

public DataSet() {
   sum = 0;
   count = 0;
   maximum = null;
   measurer=new Measurer<T>() {
      public double measure(T m) {return m.getMeasure(); }
   }
}


Così facendo però non puoi più lavorare con oggetti che non sono Measurable. Niente di grave, se volessi continuare a lavorare con gli oggetti java.awt.Rectangle (che non implementano ovviamente l'interfaccia Measurable) potresti "adattarli" in modo che implementino quell'interfaccia.
In questo modo:

Codice: Seleziona tutto

public class MeasurableRectangle implements Measurable {
    private Rectangle rectangle;
    public MeasureableRectangle(int x, int y, int w, int h) {
        rectangle=new Rectangle(x,y,w,h);
    }

    public int getWidth() { return rectangle.getWidth(); }
    public int getHeight() { return rectangle.getHeight(); }

    public double getMeasure() {
        return getWidth()*getHeight();
    }
}


Per maggiori info, dai un'occhiata al pattern adapter.
mvinci
New Entry
Messaggi: 5
Iscritto il: 11 ago 2017, 10:10

Re: Esercizio interfacce Measurable e Measurer

Messaggioda mvinci » 12 ago 2017, 13:08

Grazie per la risposta xneo; premetto che sono neofita di Java e della programmazione in generale.
Secondo me manca qualcosa. Non è che per caso DataSet dovrebbe essere generico in un tipo T sottotipo Measurable?
Cioè

Codice: Seleziona tutto

public class DataSet<T extends Measurable> {...}


Così come Measurer, l'avrei scritto come Measurer<T>, di conseguenza il metodo sarebbe stato measure(T measuree)


Non credo che manchi qualcosa nella classe DataSet dato che è presente nel capitolo del manuale da cui è tratto l'esercizio; inoltre le classi generiche non sono state ancora trattate tranne che per un breve accenno quando è stata introdotta la classe ArrayList, quindi dovrei risolvere l'esercizio senza utilizzarle.

se volessi continuare a lavorare con gli oggetti java.awt.Rectangle (che non implementano ovviamente l'interfaccia Measurable) potresti "adattarli" in modo che implementino quell'interfaccia.
In questo modo:

Codice: Seleziona tutto

public class MeasurableRectangle implements Measurable {
    private Rectangle rectangle;
    public MeasureableRectangle(int x, int y, int w, int h) {
        rectangle=new Rectangle(x,y,w,h);
    }

    public int getWidth() { return rectangle.getWidth(); }
    public int getHeight() { return rectangle.getHeight(); }

    public double getMeasure() {
        return getWidth()*getHeight();
    }
}


Per lavorare con gli oggetti Rectangle è stata creata anch'essa nel capitolo del manuale la classe RectangleMeasurer che implementa Measurer che ho scritto nel primo post; da quel che ho capito DataSet dovrebbe essere modificata per lavorare con classi scritte ex novo che possono quindi implementare Measurable. Un esempio di tale classe è la classe BankAccount

Codice: Seleziona tutto

/**
 * Un conto bancario ha un saldo che può essere modificato da depositi e prelievi
 */
public class BankAccount implements Measurable
{
    //campi
    private int accountNumber;
    private double balance;
   
   
    /**
     * Costruisce un conto bancario con saldo uguale a zero.
     * @param anAccountNumber il numero di questo conto bancario
     */
    public BankAccount(int anAccountNumber)
    {
        accountNumber = anAccountNumber;
        balance = 0;
    }
   
    /**
     * Costruisce un conto bancario con un saldo assegnato.
     * @param anAccountNumber il numero di questo conto bancario
     * @param initialBalance il saldo iniziale
     */
    public BankAccount(int anAccountNumber, double initialBalance)
    {
        accountNumber = anAccountNumber;
        balance = initialBalance;
    }
   
    public double getMeasure()
    {
        return balance;
    }
   
    /**
     * Restituisce il numero del conto.
     * @return il numero del conto
     */
    public int getAccountNumber()
    {
        return accountNumber;
    }
   
     /**
     * Versa denaro nel conto bancario.
     * @param amoun l'importo da versare
     */
    public void deposit(double amount)
    {
        double newBalance = balance + amount;
        balance = newBalance;
    }
   
    /**
     * Preleva denaro dal conto bancario.
     * @param amount l'importo da prelevare
     */
    public void withdraw(double amount)
    {
        double newBalance = balance - amount;
        balance = newBalance;
    }
   
    /**
     * Ispeziona il valore del saldo attuale del conto bancario.
     * @return il saldo attuale
     */
    public double getBalance()
    {
        return balance;
    }
   
}


Ciò che segue è un test di BankAccount con DataSet

Codice: Seleziona tutto

/**
 * Collauda la classe DataSet
 */

public class DataSetTester
{
    public static void main(String[] args)
    {
        DataSet datiBanca = new DataSet();
       
        datiBanca.add(new BankAccount(0,0));
        datiBanca.add(new BankAccount(1,10000));
        datiBanca.add(new BankAccount(2,2000));
       
        System.out.println("Average Balance = " + datiBanca.getAverage());
        Measurable max = datiBanca.getMaximum();
       
        System.out.println("Highest Balance = " + max.getMeasure());   
    }
}


Output di DataTester
Average Balance = 4000.0
Highest Balance = 10000.0

Per maggiori info, dai un'occhiata al pattern adapter.

I pattern adapter non li conosco, ho dato uno sguardo a wikipedia ma sembra un argomento avanzato per me.
mvinci
New Entry
Messaggi: 5
Iscritto il: 11 ago 2017, 10:10

Re: Esercizio interfacce Measurable e Measurer

Messaggioda mvinci » 12 ago 2017, 13:17

In rete(premetto non su un forum) ho trovato una parziale risoluzione dell'esercizio che comprende la classe tester

Codice: Seleziona tutto

import java.awt.Rectangle;

/**
   This program tests a data set that can be used with measurers
   and measurables.
*/
public class DataSetTester
{
   public static void main(String[] args)
   {
      class RectangleMeasurer implements Measurer
      {
         public double measure(Object anObject)
         {
            Rectangle aRectangle = (Rectangle) anObject;
            double area = aRectangle.getWidth() * aRectangle.getHeight();
            return area;
         }
      }

      Measurer m = new RectangleMeasurer();

      DataSet d = new DataSet(m);
     
      d.add(new Rectangle(5, 10, 20, 30));
      d.add(new Rectangle(10, 20, 30, 40));
      d.add(new Rectangle(20, 30, 5, 10));

      System.out.println("Average area: " + d.getAverage());
      System.out.println("Expected: 616.6666667");

      Object max = d.getMaximum();
      System.out.println("Largest area: " + m.measure(max));
      System.out.println("Expected: 1200");

      // Test the default Measurer
     
      d = new DataSet();

      d.add(new BankAccount(2000));
      d.add(new BankAccount(200));
      d.add(new BankAccount(20000));

      System.out.println("Average balance: " + d.getAverage());
      System.out.println("Expected: 7400");
      Measurable max2 = (Measurable) d.getMaximum();
      System.out.println("Highest balance: " + max2.getMeasure());
      System.out.println("Expected: 20000");
   }
}


la parte che interessa è quella dopo il commento "//test the default Measurer"
xneo
Utente Semi-Senior
Messaggi: 187
Iscritto il: 12 mar 2013, 14:02

Re: Esercizio interfacce Measurable e Measurer

Messaggioda xneo » 12 ago 2017, 13:59

Allora se non vuoi utilizzare i generics, l'unica soluzione è scrivere il costruttore come segue

Codice: Seleziona tutto

public DataSet() {
    ---
    measurer=new Measurer() { //anonymous inner class
        public double measure(Object o) {
            if(!(o instanceof Measurable)) throw IllegalArgumentException("Il parametro deve implementare l'interfaccia Measurable");
            return ((Measurable)o).getMeasure();
        }
    }
}


Per quanto riguarda il pattern adapter, capisco che magari può essere un argomento un po' avanzato per un neofita, consente di "adattare" (o "wrappare") una classe, facendole implementare una interfaccia, in modo che sia compatibile per essere utilizzata per i nostri scopi.

E' ovvio che se scrivi tu una classe, "ex novo" puoi farle implementare direttamente l'interfaccia Measurable, ma se non puoi modificare il sorgente di una classe già esistente, come appunto la classe Rectangle del package java.awt, il pattern ti consente di poter adattare la classe Rectangle in modo che una sua istanza possa essere utilizzata laddove ci si aspetta un'istanza di tipo Measurable.

ATTENZIONE:
La classe RectangleMeasurer implementa Measurer, quindi non è ne un Measurable.
La classe MeasurableRectangle è un Measurable.

PS: Come scoprirai più avanti, l'interfaccia Measurer è una interfaccia funzionale (prova a inserire l'annotazione @FunctionalInterface prima della definizione dell'interfaccia) e quindi, da Java 8, è possibile usare al posto dell'anonymous inner class una lambda expression. Qundi avrei potuto scrivere:

Codice: Seleziona tutto

measurer=(o) -> {
    if(!(o instanceof Measurable)) throw IllegalArgumentException("Il parametro deve implementare l'interfaccia Measurable");
    return ((Measurable)o).getMeasure();
}
mvinci
New Entry
Messaggi: 5
Iscritto il: 11 ago 2017, 10:10

Re: Esercizio interfacce Measurable e Measurer

Messaggioda mvinci » 12 ago 2017, 15:19

Allora se non vuoi utilizzare i generics, l'unica soluzione è scrivere il costruttore come segue

Codice: Seleziona tutto

public DataSet() {
    ---
    measurer=new Measurer() { //anonymous inner class
        public double measure(Object o) {
            if(!(o instanceof Measurable)) throw IllegalArgumentException("Il parametro deve implementare l'interfaccia Measurable");
            return ((Measurable)o).getMeasure();


Questo costruttore funziona però non mi è ben chiaro cosa succede.
Ad esempio scrivere la classe interna tra new Measurer() e ";" è un procedimento che non conosco non essendo stato illustrato nel manuale e mi viene il dubbio che esista una soluzione ancora più semplice; ad esempio per questo esercizio si può tranquillamente fare a meno di gestire le eccezioni e credo anche di usare "instanceof"(ancora una volta argomenti non affrontati).
xneo
Utente Semi-Senior
Messaggi: 187
Iscritto il: 12 mar 2013, 14:02

Re: Esercizio interfacce Measurable e Measurer

Messaggioda xneo » 12 ago 2017, 16:38

Le classi interne le vedrai più avanti. La soluzione più semplice è quella che fa uso della lambda expression, per il momento a te serve la soluzione più verbosa.
Quindi anzichè usare un'anonymous inner class, scrivi una classe MeasurableMeasurer che implementa Measurer in questo modo

Codice: Seleziona tutto

class MeasurableMeasurer implements Measurer {
    public double measure(Object o) {
        return ((Measurable)o).getMeasure();
    }
}


e quindi nel costruttore puoi scrivere

Codice: Seleziona tutto

 measurer=new MeasurableMeasurer();


Come puoi notare, non ho usato ne instanceof, ne ho lanciato l'eccezione IllegalArgumentException(). Quello che succede, però, se come parametro di measure() non passi un Measurable, è che ti becchi una ClassCastException().
Ultima modifica di xneo il 13 ago 2017, 08:59, modificato 1 volta in totale.
mvinci
New Entry
Messaggi: 5
Iscritto il: 11 ago 2017, 10:10

Re: Esercizio interfacce Measurable e Measurer

Messaggioda mvinci » 12 ago 2017, 21:37

MeasurableMeasure dovrebbe implementare Measurer e non Measurable giusto? infatti hai implementato il metodo measure

Quello che succede, però, se come parametro di measure() non passi un Measurable, è che ti becchi una ClassCastException().

Nella norma, in questi esercizi è responabilità dello studente passare i parametri giusti, immagino che verranno trattate più avanti le soluzioni per questi problemi.

Per il resto tutto ok, l'esercizio mi sembra risolto anche sfruttando gli elementi messi a disposizione fino ad ora dal manuale, grazie ancora.

P.S.
Ci sono esercizi molto simili nel capitolo, se dovessi riscontrare altre difficoltà le posterò qui :)
xneo
Utente Semi-Senior
Messaggi: 187
Iscritto il: 12 mar 2013, 14:02

Re: Esercizio interfacce Measurable e Measurer

Messaggioda xneo » 13 ago 2017, 08:54

MeasurableMeasure dovrebbe implementare Measurer e non Measurable giusto? infatti hai implementato il metodo measure


Si, si, scusami, intendevo Measurer.

Torna a “Java”

Chi c’è in linea

Visitano il forum: Bing [Bot] e 2 ospiti