7
\$\begingroup\$

This is my first attempt at a program (outside of school) and I was trying to implement and learn polymorphism on a conversion calculator for volumes and weights.

I found myself using long switch statements in the GUI to test user inputs against the units in order to make the calculations. Is there a better alternative to using switch statements here that might shorten the code, make it more readable and possibly improve efficiency?

Did I make good use of polymorphism?

I am open to any constructive criticism but again, I'm learning so please offer an explanation with your criticism/suggestions.

public class ConversionLayout {
    NumberFormat df = new DecimalFormat("#.#####");
    private JRadioButton volume = new JRadioButton("volume");
    private JRadioButton weight = new JRadioButton("weight");
    private JComboBox<String> selection1 = new JComboBox<String>();
    private JComboBox<String> selection2 = new JComboBox<String>();
    private DefaultComboBoxModel<String> unitSet1 = new DefaultComboBoxModel<String>();
    private DefaultComboBoxModel<String> unitSet2 = new DefaultComboBoxModel<String>();
    private JTextField userInput1;
    private JTextField userInput2;
    private JLabel topLabel;
    private JLabel equals;
    private JPanel topPanel;
    private JPanel calcInput;
    private JPanel radioPanel;
    private String[] units = { "ml", "litre", "oz", "cup", "pint", "tbsp",
        "tsp", "gallon", "quart" };
    private String[] units1 = { "g", "kg", "oz", "lb" };
    private String unit1;
    private String unit2;
    private String result;
    private double amountValue;
    private Conversion ml = new ConvertFromMillilitre();
    private Conversion cup = new ConvertFromCup();
    private Conversion gallon = new ConvertFromGallon();
    private Conversion litre = new ConvertFromLitre();
    private Conversion oz = new ConvertFromOz();
    private Conversion pint = new ConvertFromPint();
    private Conversion quart = new ConvertFromQuart();
    private Conversion tbsp = new ConvertFromTbsp();
    private Conversion tsp = new ConvertFromTsp();
    private Conversion g = new ConvertFromGram();
    private Conversion kg = new ConvertFromKilogram();
    private Conversion ozWeight = new ConvertFromOzWeight();
    private Conversion lb = new ConvertFromPound();

public ConversionLayout() {
    equals = new JLabel("=");
    equals.setHorizontalAlignment(SwingConstants.CENTER);
    userInput1 = new JTextField();
    userInput2 = new JTextField();

    for (int i = 0; i < units.length; i++) {
        unitSet1.addElement(units[i]);
        unitSet2.addElement(units[i]);
    }

    selection1.setModel(unitSet1);
    selection2.setModel(unitSet2);
    ButtonGroup group = new ButtonGroup();
    group.add(volume);
    group.add(weight);
    volume.setSelected(true);
    radioPanel = new JPanel(new GridLayout(0, 2));
    radioPanel.add(volume);
    radioPanel.add(weight);

    volume.addActionListener(new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {
            unitSet1.removeAllElements();
            unitSet2.removeAllElements();
            for (int i = 0; i < units.length; i++) {
                unitSet1.addElement(units[i]);
                unitSet2.addElement(units[i]);
            }
        }

    });

    weight.addActionListener(new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {
            unitSet1.removeAllElements();
            unitSet2.removeAllElements();
            for (int i = 0; i < units1.length; i++) {
                unitSet1.addElement(units1[i]);
                unitSet2.addElement(units1[i]);
            }
        }

    });

    setDefaultValues("ml", "ml");

    calcInput = new JPanel(new GridLayout(1, 5));

    calcInput.add(selection1);
    calcInput.add(userInput1);
    calcInput.add(equals);
    calcInput.add(selection2);
    calcInput.add(userInput2);

    topLabel = new JLabel("Please enter your selections below:");
    topPanel = new JPanel();
    topPanel.add(topLabel);

    unitSelections();

    UserEntries entry = new UserEntries();
    userInput1.addActionListener(entry);
    userInput2.addActionListener(entry);

}// end layout constructor

private void setDefaultValues(String val1, String val2) {
    if (unit1 == null) {
        this.unit1 = val1;
        this.unit2 = val2;
    }
}

// ItemListener to set the value of the unit1 and unit2 fields based on user
// selections
private void unitSelections() {
    selection1.addItemListener(new ItemListener() {

        @Override
        public void itemStateChanged(ItemEvent e) {
            if (e.getStateChange() == ItemEvent.SELECTED) {
                String selection = selection1.getSelectedItem().toString();
                unit1 = selection;
            }
        }
    });

    selection2.addItemListener(new ItemListener() {

        @Override
        public void itemStateChanged(ItemEvent e) {
            if (e.getStateChange() == ItemEvent.SELECTED) {
                String selection = selection2.getSelectedItem().toString();
                unit2 = selection;
            }
        }
    });
}

// sets the values for userInput to each conversion unit
private void setValues(double userInput) {
    ml.setValue(userInput);
    ml.getValue();

    cup.setValue(userInput);
    cup.getValue();

    gallon.setValue(userInput);
    gallon.getValue();

    litre.setValue(userInput);
    litre.getValue();

    oz.setValue(userInput);
    oz.getValue();

    pint.setValue(userInput);
    pint.getValue();

    quart.setValue(userInput);
    quart.getValue();

    tbsp.setValue(userInput);
    tbsp.getValue();

    tsp.setValue(userInput);
    tsp.getValue();

    tsp.setValue(userInput);
    tsp.getValue();

    g.setValue(userInput);
    g.getValue();

    kg.setValue(userInput);
    kg.getValue();

    ozWeight.setValue(userInput);
    ozWeight.getValue();

    lb.setValue(userInput);
    lb.getValue();
}


// class for setting user input values on either JTextField
private class UserEntries implements ActionListener {

    @Override
    public void actionPerformed(ActionEvent e) {
        //if the volume JRadioButton is selected we will run the switch on volume units.
        if (volume.isSelected()) {
            // if userInput1 triggers ActionEvent we check for unit1 value and
            // convert to unit2 value
            if (e.getSource() == userInput1) {
                String value = userInput1.getText();
                // ensure entry is a numerical value
                try {
                    amountValue = Double.parseDouble(value);
                    if (amountValue < 0) {
                        throw new NumberFormatException();
                    }
                } catch (NumberFormatException ex) {
                    JOptionPane.showMessageDialog(userInput1,
                            "Please enter a numeric value to convert.");
                }
                // sets the userInput value to the unit1
                setValues(amountValue);
                switch (unit1) {
                case "ml":
                    result = String.valueOf(ml.convertTo(unit2));
                    userInput2
                            .setText(df.format(Double.parseDouble(result)));
                    break;
                case "cup":
                    result = String.valueOf(cup.convertTo(unit2));
                    userInput2
                            .setText(df.format(Double.parseDouble(result)));
                    break;
                case "gallon":
                    result = String.valueOf(gallon.convertTo(unit2));
                    userInput2
                            .setText(df.format(Double.parseDouble(result)));
                    break;
                case "litre":
                    result = String.valueOf(litre.convertTo(unit2));
                    userInput2
                            .setText(df.format(Double.parseDouble(result)));
                    break;
                case "oz":
                    result = String.valueOf(oz.convertTo(unit2));
                    userInput2
                            .setText(df.format(Double.parseDouble(result)));
                    break;
                case "pint":
                    result = String.valueOf(pint.convertTo(unit2));
                    userInput2
                            .setText(df.format(Double.parseDouble(result)));
                    break;
                case "quart":
                    result = String.valueOf(quart.convertTo(unit2));
                    userInput2
                            .setText(df.format(Double.parseDouble(result)));
                    break;
                case "tbsp":
                    result = String.valueOf(tbsp.convertTo(unit2));
                    userInput2
                            .setText(df.format(Double.parseDouble(result)));
                    break;
                case "tsp":
                    result = String.valueOf(tsp.convertTo(unit2));
                    userInput2
                            .setText(df.format(Double.parseDouble(result)));
                    break;

                }//end switch

                // if userInput2 triggers ActionEvent we check for unit2
                // value
                // and convert to unit1 value
            } else if (e.getSource() == userInput2) {
                String value = userInput2.getText();
                try {
                    amountValue = Double.parseDouble(value);
                    if (amountValue < 0) {
                        throw new NumberFormatException();
                    }
                } catch (NumberFormatException ex) {
                    JOptionPane
                            .showMessageDialog(userInput2,
                                    "Please enter a positive numeric value to convert.");
                }
                // sets the userInput value to the units
                setValues(amountValue);
                switch (unit2) {
                case "ml":
                    result = String.valueOf(ml.convertTo(unit1));
                    userInput1
                            .setText(df.format(Double.parseDouble(result)));
                    break;
                case "cup":
                    result = String.valueOf(cup.convertTo(unit1));
                    userInput1
                            .setText(df.format(Double.parseDouble(result)));
                    break;
                case "gallon":
                    result = String.valueOf(gallon.convertTo(unit1));
                    userInput1
                            .setText(df.format(Double.parseDouble(result)));
                    break;
                case "litre":
                    result = String.valueOf(litre.convertTo(unit1));
                    userInput1
                            .setText(df.format(Double.parseDouble(result)));
                    break;
                case "oz":
                    result = String.valueOf(oz.convertTo(unit1));
                    userInput1
                            .setText(df.format(Double.parseDouble(result)));
                    break;
                case "pint":
                    result = String.valueOf(pint.convertTo(unit1));
                    userInput1
                            .setText(df.format(Double.parseDouble(result)));
                    break;
                case "quart":
                    result = String.valueOf(quart.convertTo(unit1));
                    userInput1
                            .setText(df.format(Double.parseDouble(result)));
                    break;
                case "tbsp":
                    result = String.valueOf(tbsp.convertTo(unit1));
                    userInput1
                            .setText(df.format(Double.parseDouble(result)));
                    break;
                case "tsp":
                    result = String.valueOf(tsp.convertTo(unit1));
                    userInput1
                            .setText(df.format(Double.parseDouble(result)));
                    break;
                }//end switch
            }//end inner if/else
        //if the weight JRadioButton is selected we will run the switch on weight units.    
        } else if (weight.isSelected()) {
            // if userInput1 triggers ActionEvent we check for unit1
            // value and convert to unit2 value
            if (e.getSource() == userInput1) {
                String value = userInput1.getText();
                // ensure entry is a numerical value
                try {
                    amountValue = Double.parseDouble(value);
                    if (amountValue < 0) {
                        throw new NumberFormatException();
                    }
                } catch (NumberFormatException ex) {
                    JOptionPane.showMessageDialog(userInput1,
                            "Please enter a numeric value to convert.");
                }
                // sets the userInput value to the unit1
                setValues(amountValue);
                switch (unit1) {
                case "g":
                    result = String.valueOf(g.convertTo(unit2));
                    userInput2
                            .setText(df.format(Double.parseDouble(result)));
                    break;
                case "kg":
                    result = String.valueOf(kg.convertTo(unit2));
                    userInput2
                            .setText(df.format(Double.parseDouble(result)));
                    break;
                case "oz":
                    result = String.valueOf(ozWeight.convertTo(unit2));
                    userInput2
                            .setText(df.format(Double.parseDouble(result)));
                    break;
                case "lb":
                    result = String.valueOf(lb.convertTo(unit2));
                    userInput2
                            .setText(df.format(Double.parseDouble(result)));
                    break;
                }//end switch
                // if userInput2 triggers ActionEvent we check for unit2
                // value and convert to unit1 value
            } else if (e.getSource() == userInput2) {
                String value = userInput2.getText();
                try {
                    amountValue = Double.parseDouble(value);
                    if (amountValue < 0) {
                        throw new NumberFormatException();
                    }
                } catch (NumberFormatException ex) {
                    JOptionPane
                            .showMessageDialog(userInput2,
                                    "Please enter a positive numeric value to convert.");
                }
                // sets the userInput value to the units
                setValues(amountValue);
                switch (unit2) {
                case "g":
                    result = String.valueOf(g.convertTo(unit1));
                    userInput1
                            .setText(df.format(Double.parseDouble(result)));
                    break;
                case "kg":
                    result = String.valueOf(kg.convertTo(unit1));
                    userInput1
                            .setText(df.format(Double.parseDouble(result)));
                    break;
                case "oz":
                    result = String.valueOf(ozWeight.convertTo(unit1));
                    userInput1
                            .setText(df.format(Double.parseDouble(result)));
                    break;
                case "lb":
                    result = String.valueOf(lb.convertTo(unit1));
                    userInput1
                            .setText(df.format(Double.parseDouble(result)));
                    break;
                }//end switch
            }//end inner if/else
        }//end outer if/else for JRadioButton selections
    }//end ActionPerformed
}// end UserEntries

private void buildGui() {
    JFrame frame = new JFrame("Conversion Calculator");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.getContentPane().setLayout(new GridLayout(4, 1, 0, 5));
    frame.getContentPane().add(topPanel);
    frame.getContentPane().add(radioPanel);
    frame.getContentPane().add(calcInput);
    frame.setSize(400, 150);
    frame.setResizable(false);
    frame.setVisible(true);
}

public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable(){
        public void run(){
            ConversionLayout run = new ConversionLayout();
            run.buildGui();
        }
    });
}//end main

}// end ConversionLayout

GitHub

\$\endgroup\$
1
\$\begingroup\$

I would use some kind of Factory for this. If want to write it short for the sake of simplicity it would look something like this:

public class ConversionFactory {

private Map<String, Conversion> conversionMapping = new HashMap<String, Conversion>();

private static final Map<String, ConversionFactory> factories=new HashMap<>();

{
    ConversionFactory volumeFactory=new ConversionFactory();
    volumeFactory.registerNewConversion("ml", new ConvertFromMillilitre());
    volumeFactory.registerNewConversion("litre", new ConvertFromLitre());
    volumeFactory.registerNewConversion("oz", new ConvertFromOz());
    volumeFactory.registerNewConversion("cup", new ConvertFromCup());
    volumeFactory.registerNewConversion("pint", new ConvertFromPint());
    volumeFactory.registerNewConversion("tbsp", new ConvertFromTbsp());
    volumeFactory.registerNewConversion("tsp", new ConvertFromTsp());
    volumeFactory.registerNewConversion("gallon", new ConvertFromGallon());
    volumeFactory.registerNewConversion("quart", new ConvertFromQuart());
    factories.put("volume", volumeFactory);
    ConversionFactory weightFactory=new ConversionFactory();
    weightFactory.registerNewConversion("g", new ConvertFromGram());
    weightFactory.registerNewConversion("kg", new ConvertFromKilogram());
    weightFactory.registerNewConversion("oz", new ConvertFromOzWeight());
    weightFactory.registerNewConversion("lb", new ConvertFromPound());
    factories.put("weight", weightFactory);

}

private void registerConversion(String key, Conversion conv) {
    conversionMapping.put(key, conv);
}

public void registerNewConversion(String key, Conversion conv) {
    registerConversion(key, conv);
}

public Conversion findConversion(String key) {
    return conversionMapping.get(key);
}

public Set<String> getAvailableConversions() {
    return conversionMapping.keySet();
}

public static ConversionFactory getConversionFactory(String type) {
    return factories.get(type);
}
}

Of course it would be probably better to separate volume and weight factory initialization to separate classes. It is also possily better to use Enums instead of plain Strings for Factory and Conversion keys. Then your switch statements could be replaced with this:

...
private void convert(String unitType, String amountValue, String unit1, String unit2) {
        Conversion volConversion = ConversionFactory.getConversionFactory(unitType).findConversion(unit1);
        volConversion.setValue(amountValue);
        volConversion.getValue();
        String result = String.valueOf(volConversion.convertTo(unit2));
        userInput2.setText(df.format(Double.parseDouble(result)));
}
...
@Override
public void actionPerformed(ActionEvent e) {
     ...
     convert("volume", amountValue, unit1, unit2);
     ...
     convert("weight", amountValue, unit2, unit1);
     ...
}
\$\endgroup\$
  • \$\begingroup\$ Thanks, I'm reading up on both Factory and Enums and I will try and implement them. You say the Enums are possibly better? Why are they better or is it just a preference thing? \$\endgroup\$ – Mac May 27 '15 at 19:00
  • \$\begingroup\$ It depends on concrete use case. You can compare Enums with Constants. Enums sometimes offer better structure, you can list them, group them, it's often easier to select concrete Enum. \$\endgroup\$ – Ondrej Bozek May 27 '15 at 19:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.