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
1 Answer 1
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);
...
}
-
\$\begingroup\$ Thanks, I'm reading up on both
Factory
andEnums
and I will try and implement them. You say theEnums
are possibly better? Why are they better or is it just a preference thing? \$\endgroup\$Mac– Mac2015年05月27日 19:00:10 +00:00Commented May 27, 2015 at 19:00 -
\$\begingroup\$ It depends on concrete use case. You can compare
Enums
withConstants
. Enums sometimes offer better structure, you can list them, group them, it's often easier to select concreteEnum
. \$\endgroup\$Ondrej Bozek– Ondrej Bozek2015年05月27日 19:47:35 +00:00Commented May 27, 2015 at 19:47
Explore related questions
See similar questions with these tags.