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

200_success
145k22 gold badges190 silver badges478 bronze badges
asked May 25, 2015 at 18:26
\$\endgroup\$

1 Answer 1

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);
 ...
}
Abbas
5,60324 silver badges40 bronze badges
answered May 26, 2015 at 10:10
\$\endgroup\$
2
  • \$\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\$ Commented May 27, 2015 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\$ Commented May 27, 2015 at 19:47

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.