Metric Conversion Algorithm without if-else or switch-case

I want to write a program that can convert one unit to another unit. Say I have 2 methods. The first method can perform metric transformations , the second method can perform weight transformations . For instance:

long km=metricConvLength(long mil,Enum.mil,Enum.km);//first method

long agirlik=metricConvWeight(long kg,Enum.mil,Enum.km);//second method

I want to use the Enum structure for these variables. My program can convert these things and opposites;

  • nautical miles
  • nautical mile
  • feet-km
  • feet-mil
  • lb kg
  • ons- gr
  • inc - cm
  • yard - m
  • knot-km

My question is: I do not want to use if-else or switch-case structs for conversions. (Because if I use if-else struct, my code looks so bad, very easy and slow.And I need more than 50 if-else struct if I use this struct.This grinds.)

Can I write an algorithm for these transformations without using if-else or switch-case. My goal is less code, more work. Any hints about the algorithm?

+4
source share
5 answers

You do not need if-then-else - in fact, you do not need control statements in your program. All you need is a lookup table - a Map , which translates your device enumeration into a double conversion factor, so multiplying the measure in units by the conversion factor that you get by units of space, and kilograms for units of weight, And vice versa, division Counters at this ratio gives you the units you need.

With this map you can do conversions for all pairs of units:

  • Look at Cs conversion factor for source blocks
  • Look at the conversion factor Cd for the target units.
  • Return value * Cs / Cd as the result.

For example, say you want to deal with meters, yards, inches, and feet. Your map will look like this:

  • m - 1.0
  • y - 0.9144
  • in - 0.0254
  • ft - 0.3048

Now let's say you want to convert 7.5 shortcuts to foot:

  • See Cs = 0.9144
  • See Cd = 0.3048
  • Calculate and return Res = 7.5 * 0.9144 / 0.3048 = 22.5
+10
source

I would suggest introducing the type "MetricValue". The history of software development is full of projects in which programmers forgot the units of values โ€‹โ€‹that they used. Desorption results are well known.

In addition, I would suggest an immutable class, as it simplifies many design aspects.

Defining a base unit helps you when you first convert all values โ€‹โ€‹to it, and then convert the value to the target device. You can also apply the matrix, but then you will need to pre-calculate the values. The size of your matrix will have a size of n ^ 2, while the conversion from the base block and only to the base block will leave only n values. If you use complex conversion formulas that are resource intensive, then that makes sense. Otherwise, the allowance will be (too) small in relation to effort.

How about this code?

 public class MetricValue { public static enum Unit { m(1.0d), y(0.9144d), in(0.0254d), ft(0.3048d); final static Unit baseUnit = m; final double perBaseUnit; private Unit(double inM) { this.perBaseUnit = inM; } public double fromBaseUnit(double value) { return value / perBaseUnit; } public double toBaseUnit(double value) { return value * perBaseUnit; } } final double value; final Unit unit; public MetricValue(double value, Unit unit) { super(); this.value = value; this.unit = unit; } public MetricValue to(Unit newUnit) { Unit oldUnit = this.unit; return new MetricValue(newUnit.fromBaseUnit(oldUnit.toBaseUnit(value)), newUnit); } @Override public String toString() { return value + " " + unit.name(); } public static void main(String[] args) { MetricValue distanceInM = new MetricValue(1, Unit.m); MetricValue distanceInFt = new MetricValue(6, Unit.ft); System.out.println(distanceInM); System.out.println(distanceInM.to(Unit.y)); System.out.println(distanceInM.to(Unit.y).to(Unit.m)); System.out.println(distanceInM.to(Unit.y).to(Unit.ft)); System.out.println(distanceInFt.to(Unit.m)); } } 

You can use the original value and create a new measure value in the calculation. You can also add operations for basic or complex arithmetic operations.

Getters here were omitted.

+2
source

Associate their relative value with the variables in your Enums (I think you have one for distances, one for weights), like enum Distances { CM(100), M(100*1000), IN(254) } , ... and then you just need to get the ratio of the values โ€‹โ€‹provided, multiply by the first parameter, and you have a result. Use a base station of 100 or 1000 for the smallest unit if you want decent accuracy.

+1
source

Select one of the blocks as the base block (one for weight, for example, gr , and another for distance, for example, m ). Then add the toBaseUnit and fromBaseUnit to your listing using conversion rates for each of your values.

There are no "ifs", and your conversion methods will look like this:

 ResultingUnit.fromBaseUnit(originalUnit.toBaseUnit(value)); 

Listing example:

 public enum Distance { METER(new BigDecimal("1.0")), // Base Unit is METER KM(new BigDecimal("1E3")), CM(new BigDecimal("1E-2")); private final static MathContext MC = new MathContext(30, RoundingMode.HALF_EVEN); private final BigDecimal conversionRatio; Distance(BigDecimal conversionRatio) { this.conversionRatio = conversionRatio; } long fromBaseUnit(BigDecimal baseUnit) { return baseUnit.divide(conversionRatio, MC).longValue(); } // returns BigDecimal to avoid rounding two times // and possible division by zero BigDecimal toBaseUnit(long originalUnit) { return BigDecimal.valueOf(originalUnit).multiply(conversionRatio); } } 

And an adapted conversion method:

 public long metricConvLength(long value, Distance orgUnit, Distance resultUnit) { return resultUnit.fromBaseUnit(orgUnit.toBaseUnit(value)); } 

Also, to be a little faster (avoiding Map searching and looping enumeration values), the main advantage of this approach is that if you ever need a more complicated conversion operation (e.g. Temperature enumeration with Celsius, Farenheit and Kelvin) you can override fromBaseUnit and toBaseUnit in the body of the enumeration value.


Working example .

I used BigDecimal for conversion factor and internal calculations to have more control over precision and rounding.

+1
source

In your Enum, you can implement the fromString method for an enum type. Suppose your name Enum is "Distances"

 private static final Map<String,Distances> stringToEnum = new HashMap<String,Distances>; 

Initialize Map:

  static{ for(Distances distance: values()) { stringToEnum.put(distance.toString(),distance); } } 

Return enumType for string:

 public static Distances getDistance(String stringValue){ return stringToEnum.get(stringValue); } 

In your methods, you can just go through:

 Distance.getDistance("feet").getCalculatedDistance() 
+1
source

Source: https://habr.com/ru/post/1490475/


All Articles