i have different classes representing units of measure (volume, weight, distance), with an enumeration with a value for storing the values ββof the base type transformation.
The problem is that there is a lot of code duplication, and I'm sure there is an elegant way to write an abstract class that would avoid it. I do not know how I should declare this class. Here is the volume class:
import java.math.BigDecimal; import lombok.Data; @Data public class Volume { public enum Unit { CUBIC_METER(new BigDecimal("1")), // CUBIC_METER // 1 m3 = 61023.7 inch3 CUBIC_INCH(new BigDecimal("61023.7")); private Unit(BigDecimal m3Value) { this.m3Value = m3Value; } public final BigDecimal m3Value; } // internally, we store the volume in m3 private final BigDecimal volumeInM3; public Volume() { volumeInM3 = BigDecimal.ZERO; } public Volume(final String volumeValue, final Unit volumeUnit) { this(new BigDecimal(volumeValue), volumeUnit); } public Volume(final BigDecimal volumeValue, final Unit volumeUnit) { if (volumeValue.signum() == 0) { volumeInM3 = BigDecimal.ZERO; } else { volumeInM3 = volumeValue.divide(volumeUnit.m3Value, NumberUtil.MC); } } /** * Return the volume in the unit given in param * @param volumeUnit * @return */ public BigDecimal getAs(final Unit volumeUnit) { return volumeInM3.multiply(volumeUnit.m3Value, NumberUtil.MC); } public Volume add(final Volume volumeToAdd) { BigDecimal newVolumeValue = volumeToAdd.volumeInM3.add(volumeInM3, NumberUtil.MC); return new Volume(newVolumeValue, Volume.Unit.CUBIC_METER); } public Volume divide(final Volume divisor) { BigDecimal newVolumeValue = volumeInM3.divide(divisor.volumeInM3, NumberUtil.MC); return new Volume(newVolumeValue, Volume.Unit.CUBIC_METER); } public boolean isZero() { if (volumeInM3.signum() == 0) return true; return false; } public boolean isEqual(final Volume another) { if (volumeInM3.compareTo(another.volumeInM3) == 0) return true; return false; } }
And here is a pretty similar weight class:
import java.math.BigDecimal; import lombok.Data; @Data public class Weight { // the value stored with enum is the value used to convert the unit to kilogramm, // wich is the reference unit public enum Unit { KILOGRAM(new BigDecimal("1")), // 1 kg = 1000 g GRAM(new BigDecimal("1000")), // 1 kg = 2.20462 pounds POUND(new BigDecimal("2.20462")); private Unit(BigDecimal kgValue) { this.kgValue = kgValue; } private final BigDecimal kgValue; } // internally, we store the weight inKg private final BigDecimal weightInKg; public Weight() { weightInKg = BigDecimal.ZERO; } public Weight(final String weightValue, final Unit weightUnit) { this(new BigDecimal(weightValue), weightUnit); } public Weight(final BigDecimal weightValue, final Unit weightUnit) { if (weightValue.signum() == 0) { weightInKg = BigDecimal.ZERO; } else { weightInKg = weightValue.divide(weightUnit.kgValue, NumberUtil.MC); } } /** * Return the weight in the unit given in param * @param weightUnit * @return */ public BigDecimal getAs(final Unit weightUnit) { return weightInKg.multiply(weightUnit.kgValue, NumberUtil.MC); } public Weight add(final Weight weightToAdd) { BigDecimal newWeightValue = weightToAdd.weightInKg.add(weightInKg, NumberUtil.MC); return new Weight(newWeightValue, Weight.Unit.KILOGRAM); } public Weight divide(final Weight divisor) { BigDecimal newWeightValue = weightInKg.divide(divisor.weightInKg, NumberUtil.MC); return new Weight(newWeightValue, Weight.Unit.KILOGRAM); } public boolean isZero() { if (weightInKg.signum() == 0) return true; return false; } public boolean isEqual(final Weight another) { if (weightInKg.compareTo(another.weightInKg) == 0) return true; return false; } }
When the volume is initialized, the user is forced to explicitly provide the device.
Volume myCubicMeter, myCubicInch; myCubicMeter = new Volume("1", Volume.Unit.CUBIC_METER); myCubicInch = new Volume("1", Volume.Unit.CUBIC_INCH);
What I'm trying to achieve is an abstract class that implements all the methods and forces the subclasses to implement an enumeration with values. What would be the right way to do this?