Esercizio interfacce Measurable e Measurer

di il
8 risposte

Esercizio interfacce Measurable e Measurer

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:

public interface Measurable
{
    double getMeasure();
}
---------------------------------------------------------
/**
 * 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
/**
 * 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
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;
    }
}
-----------------------------------------------------------------------------
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
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.

8 Risposte

  • Re: Esercizio interfacce Measurable e Measurer

    Secondo me manca qualcosa. Non è che per caso DataSet dovrebbe essere generico in un tipo T sottotipo Measurable?
    Cioè
    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
    
    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:
    
    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.
  • Re: Esercizio interfacce Measurable e Measurer

    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è
    
    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:
    
    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
    
    /**
     * 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
    /**
     * 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.
  • Re: Esercizio interfacce Measurable e Measurer

    In rete(premetto non su un forum) ho trovato una parziale risoluzione dell'esercizio che comprende la classe tester
    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"
  • Re: Esercizio interfacce Measurable e Measurer

    Allora se non vuoi utilizzare i generics, l'unica soluzione è scrivere il costruttore come segue
    
    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:
    
    measurer=(o) -> {
        if(!(o instanceof Measurable)) throw IllegalArgumentException("Il parametro deve implementare l'interfaccia Measurable");
        return ((Measurable)o).getMeasure();
    }
    
  • Re: Esercizio interfacce Measurable e Measurer

    Allora se non vuoi utilizzare i generics, l'unica soluzione è scrivere il costruttore come segue
    
    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).
  • Re: Esercizio interfacce Measurable e Measurer

    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
    
    class MeasurableMeasurer implements Measurer {
        public double measure(Object o) {
            return ((Measurable)o).getMeasure();
        }
    }
    
    e quindi nel costruttore puoi scrivere
     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().
  • Re: Esercizio interfacce Measurable e Measurer

    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
  • Re: Esercizio interfacce Measurable e Measurer

    MeasurableMeasure dovrebbe implementare Measurer e non Measurable giusto? infatti hai implementato il metodo measure
    Si, si, scusami, intendevo Measurer.
Devi accedere o registrarti per scrivere nel forum
8 risposte