Domanda [AIUTO] Costrutto if

Stato
Discussione chiusa ad ulteriori risposte.

carapace

Utente Bronze
22 Luglio 2017
21
3
0
27
Salve a tutti, scrivendo un semplice programmino per il calcolo dell'indice di massa corporea mi sorge il dubbio se il modo in cui stia scrivendo il tutto, usando il costrutto if, sia il più adatto alla situazione o meno. Mi sembra ridondante e poco compatto ma non mi viene in mente in quale altro modo scrivere la cosa. Allego una parte del codice incriminato di seguito:

Java:
public String risultatoTesto (){
        String situazionePeso;
        if (getSesso() == "Maschio" && getEtà() >= 18 && getEtà() <= 24 && risultatoNumerico() < 16){
            situazionePeso = "Grave magrezza (BMI <16)";
        }
        else if (getSesso() == "Maschio" && getEtà() >= 18 && getEtà() <= 24 && risultatoNumerico() >= 16.0 && risultatoNumerico() < 18.50){
            situazionePeso = "Visibilmente Sottopeso (BMI 16-18.5)";
        }
        else if (getSesso() == "Maschio" && getEtà() >= 18 && getEtà() <= 24 && risultatoNumerico() >= 18.50 && risultatoNumerico() < 20.00){
             situazionePeso = "Leggermente Sottopeso (BMI 18.5-20)";
        }
        else if (getSesso() == "Maschio" && getEtà() >= 18 && getEtà() <= 24 && risultatoNumerico() >= 20.00 && risultatoNumerico() <= 25.00){
            situazionePeso = "Normopeso (BMI 20-25)";
        }
        else if (getSesso() == "Maschio" && getEtà() >= 18 && getEtà() <= 24 && risultatoNumerico() > 25.00 && risultatoNumerico() <= 30){
            situazionePeso = "Sovrappeso (BMI 25-30)";
        }
else {
    situazionePeso = "IGNOTA!";
}
return situazionePeso;
 
mi sorge il dubbio se il modo in cui stia scrivendo il tutto, usando il costrutto if, sia il più adatto alla situazione o meno.
Innanzitutto il contenuto delle stringhe (e degli oggetti in generale) si confronta per uguaglianza tramite equals() ... NON con l'operatore == (che si basa solo sulla "identità" degli oggetti).

Poi comunque ci sono evidenti duplicazioni (es. il getEtà() >= 18 && getEtà() <= 24) che potresti tranquillamente evitare se ristrutturassi i test più in "cascata" rispetto a come hai adesso.
E puoi anche abbreviare facendo un metodo che testa se l'età è in un certo range.

I test sono solo quelli mostrati? O ce ne saranno altri? E per le altre età??


P.S.: evita di usare lettere accentate getEtà() a meno che ci siano necessità veramente specifiche e particolari.
P.S.2: per il sesso sarebbe stato forse meglio una enum piuttosto che una stringa.
 
Cosa intendi per "più a cascata"?
I test sono questi, devo solo aggiungere le altre età e sesso femminile.
Grazie mille per tutte le indicazioni, gentilissimo e chiaro :)
 
Non sono un esperto in cure dimagranti e BMI, ma mi pare che eta' [e forse anche sesso] non centrino molto con l risultato finale:
BMI = Peso in kg/ Altezza * altezza , e influisce di ben poco l' eta' :
se sono un 13enne alto 2 metri [possibile], il mio BMI non puo' variare rispetto ad uno che ha 20 anni ed e' alto 2 metri, al massimo il metabolismo, ma questo e' un altro fatto [o almeno penso, se sbaglio dimmelo cosi' divento meno ignorante di quello che sono in medicina]. Inoltre il fatto del sesso anche non ha molto a che fare: mi pare che l' indice cambi di circa 1/1.5, quindi un mascio con "IBM = 20" dovrebbe equivalere ad una donna di "IBM=18.5" [al massimo, e sempre se non sbaglio].
Comunque, se fai come l' hai pensato, dovrai fare almeno 10 combinazioni [se lo vuoi solo per maschi/femmine tra i 18/24], mentre una cosa molto piu' semplice, come questo [se non ho sbagliato i criteri e se cio' che ho detto fin ora e' giusto, dovrebbe essere qualcosa del genere]:
Java:
 protected final class BMICalculator {
  
        static void reportBMICondition(double bMI){
            if(bMI<16)    System.out.println("Grave magrezza");
            if(bMI>=16 && bMI<=18.5) System.out.println("Visibilmente Sottopeso");
            if(bMI>18.5 && bMI<=20) System.out.println("Leggermente Sottopeso");
            if(bMI>20 && bMI<=25) System.out.println("Normopeso");
            if(bMI>25 && bMI<=30) System.out.println("Sovrappeso");
        }
     
    public static void main (String [] args) {
        Scanner input = new Scanner(System.in).useLocale(Locale.US);
      
        float altezza;
        float peso;
        double bMI;
      
        System.out.print ("Inserisci peso in Kg: ");
        peso = input.nextFloat();
        System.out.print ("Inserisci peso in Metri: ");
        altezza = input.nextFloat();
        bMI = peso / Math.pow(altezza, 2);
        System.out.printf ("Il tuo indice BMI e' di "+ bMI);
      
        reportBMICondition(bMI);
    }

Ovviamente un approccio ad oggetti e' molto meglio ed anche piu' gradevole per riusabilita' e manutenzione, ma su cosette del genere non fa questo la differenza.
Usare un enum sarebbe molto meglio e anche piu' facile da usare
 
Cosa intendi per "più a cascata"?
A parte il termine, comunque intendevo qualcosa del tipo:

Codice:
if (getSesso().equals("Maschio")) {
    if (getEtà() >= 18 && getEtà() <= 24) {
        // qui solo i test su risultatoNumerico()
    } else if ( ...altro range di età... ) {
        // ....
    }
} else if (getSesso().equals("Femmina")) {
    if ( ...test su un range di età... ) {
        // qui altri test su risultatoNumerico()
    }
}

In questo modo, perlomeno, eviti ripetizioni. Per i maschi non ripeti 5 volte il test sul sesso e nemmeno il test getEtà() >= 18 && getEtà() <= 24

Se hai altri range e per ciascuno le condizioni su risultatoNumerico() sono differenti .... beh, certo, diventa un "bel" if corposo.
 
Eh nono, età e sesso influiscono eccome. Lasciamo fare i giovanissimi sotto i 18 che sono un caso a parte (come anche i palestrati che per la massa muscolare risulterebbero obesi su una tabella BMI anche se in effetti non lo sono). La formula base è peso / altezza*altezza come hai detto te ma se poi vuoi rendere il calcolo più esatto e preciso, aggiungi gli altri due dati detti. Per un uomo di età 18-24 il BMI è considerato normale tra un range di 20 e 25, una donna della stessa età che quindi avrà una muscolatura ed una struttura diversa per via del sesso sarà in un range normale tra 19 e 24. Te dirai "eeeeh, che differenza fa un punticino?". La fa :D.
Lo stesso cambia a seconda della fascia di età. Un quarantenne avrà un range normopeso un po' più alto rispetto ad un ventenne. I fattori in realtà sarebbero tanti di più, più ne metti e più preciso sarà il risultato.
 
Non mi ricordo più tanto bene il Java, ma ti consiglio di usare un costrutto switch invece che tanti if
:D
Il costrutto switch e' piu' utile per una sola cosa da filtrare con piu' opzioni e valori prestabiliti, in quel caso anziche' scrivere:

Java:
if(option ==1)
    System.out.println("Comando 1");
if(option == 2)
    System.out.println("Comando 2");
if(option == 3)
    System.out.println("Comando 3");
, e converebbe fare allora:
Java:
switch(option){
    case 1: {...}
     case 3: {....}
    case 2: {}
}
, ma per l' uso che deve fare lui, meglio un if ben strutturato che filtri prima per genere, poi per age e infine per result piuttosto che fare una cosa come uno switch con 20 casi ....

Inoltre se vogliamo essere piu' tecnici: un if e' un espressione che deve valutare tramite valore boolean se una determinata situazione/valore e' verificato, mentre uno switch e' solamente una verifica del valore, senza pero' valutazione vera e propria, tanto che puoi scrivere:

Java:
if(age < isMaggiorenne())
     System.out.println("Sei maggiorenne");
else
      System.out.println("Non sei maggiorenne");
, ma non puoi valutare l espressione di sopra con uno switch:
Java:
switch(age){
    case 18 : {}
     ecc....
}
, o meglio, in verita' viene valutata come se fosse stato scritto:
Java:
if(age == 18)
    ecc...
, ma di fatto non hai molte altre opzioni, tanto che non puo usare un boolean dato da un espressione di comparazione da te formulata come con un semplice if.
O al massimo l uso dello switch sarebbe solo "forzato" , ma di fatto un bell if penso sia migliore [in questa situazione si intende sempre]
 
Una possibile soluzione più object oriented per risolvere questo problema la si ha con il pattern strategy. Visto che è una cosa relativamente carina e dato che dubito che dicendo "pattern strategy" ci sia molta gente che sappia di cosa sto parlando, vi passo direttamente il codice.

Java:
public enum BodyStatus {
    EXTREMELY_UNDERWEIGHT("Grave magrezza"),
    UNDERWEIGHT("Visibilmente sottopeso"),
    SLIGHTLY_UNDERWEIGHT("Leggermente sottopeso"),
    NORMALWEIGHT("Normopeso"),
    SLIGHTLY_OVERWEIGHT("Leggermente sovrappeso"),
    OVERWEIGHT("Obeso"),
    EXTREMELY_OVERWEIGHT("Grave obesità");

    private final String description;
    private BodyStatus(String value) {
        description = value;
    }

    @Override
    public String toString() {
        return description;
    }
}

public interface Person {
    public BodyStatus getBodyStatus(float bmi);
}

public class YoungMale implements Person {
    public BodyStatus getBodyStatus(float bmi) {
        if (bmi < 16.0)      return BodyStatus.EXTREMELY_UNDERWEIGHT;
        else if (bmi < 18.5) return BodyStatus.UNDERWEIGHT;
        else if (bmi < 20.0) return BodyStatus.SLIGHTLY_UNDERWEIGHT;
        else if (bmi < 25.0) return BodyStatus.NORMALWEIGHT;
        // else if (bmi < ??.?) return BodyStatus.SLIGHTLY_OVERWEIGHT;
        else if (bmi < 30.0) return BodyStatus.OVERWEIGHT;
        else                 return BodyStatus.EXTREMELY_OVERWEIGHT;
    }
}

public class BMICalculator {
    public static void main (String [] args) {
        float height = Float.parseFloat(System.console().readLine("Inserisci altezza in mt: "));
        float weight = Float.parseFloat(System.console().readLine("Inserisci peso in kg: "));
        float bmi = weight / (height * height);
        String sex = System.console().readLine("Inserisci sesso (m / f): ");
        int age = Integer.parseInt(System.console().readLine("Inserisci età: "));

        // Creazione di una persona (parte da modificare)
        Person person = null;
        if (sex.toUpperCase().startsWith("M")) {
            // TeenMale ?
            if (age >= 18 && age <= 24) person = new YoungMale();
            // AdultMale ?
        }

        if (person != null) System.out.println("Il tuo BMI è " + bmi + " e risulti essere: " + person.getBodyStatus(bmi));
        else System.out.println("Il tuo BMI è " + bmi + "");
    }
}

In questo esempio funziona solo per i maschi di età tra i 18 e i 24, ma supponiamo di volerlo far funzionare anche per qualcun altro (eg. le femmine della stessa età). Cosa bisogna fare? Crei una nuova classe che implementa l'interfaccia person e aggiungi un if al main.
A dire il vero qui non abbiamo eliminato poi così tanti if, li abbiamo solo spostati. Ci sono altri modi di procedere che ti permetterebbero veramente di togliere gli if e di scrivere un codice più corto, ma ho voluto cogliere l'occasione per introdurti questo design pattern: non abbiamo tolto tanti if, ma il codice è molto più elegante, estendibile e manutenibile (e soprattutto stiamo facendo un uso sensato dell'object orientation, cosa che in questo forum si vede poco).
Se hai qualche domanda, chiedi pure.
 
In effetti avevo/avevamo completamente tralasciato l'aspetto object oriented.
Non conosco i metodi Float.parseFloat() e System.console() (sono ancora alle prime armi), ma me li guardo subito. Il resto tutto abbastanza chiaro, grazie :)
 
Stato
Discussione chiusa ad ulteriori risposte.