这是用不同单位实现距离的好方法

时间:2016-05-11 10:35:38

标签: java design-patterns distance

我正在寻找一些好的模式,有可能在不同的单位表达距离。我找到了Martin Fowler article about quantities,我编写了类似的东西:

这是距离类(我认为抽象它不是必要的):

public class Distance {
    double mValue;
    DistanceUnit mUnit;

    public Distance(double value, DistanceUnit unit){
        this.mValue = value;
        this.mUnit = unit;
    }

    public Distance toUnit(DistanceUnit unit){
        double factor = this.mUnit.getMetresFactor()/unit.getMetresFactor();
        double newValue = this.mValue * factor;
        Distance distance = new Distance(newValue, unit);
        return distance;
    }

    @Override
    public String toString(){
        return String.valueOf(mValue);
    }
}

看起来很简单。转化toUnit基于DistanceUnit方法getMetresFactor。每个Unit类实现DistanceUnit接口,方法getMetresFactor()如:

public interface DistanceUnit {
    double getMetresFactor();
}


public class Inch implements DistanceUnit {
    @Override
    public double getMetresFactor() {
        return 0.0254;
    }
}


public class Kilometer implements DistanceUnit {
    @Override
    public double getMetresFactor() {
        return 1000.0;
    }
}

用法例如:

Distance inches = new Distance(300.0, new Inch());
Distance kilometres = inches.toUnit(new Kilometres());

因此它返回正确的值。

以这种方式存储距离是否很好?也许你知道这种方法的一些弱点。也许这是一个好主意,在这里使用FactoryMethod模式来构建基于单位快捷方式的距离,例如" m"对于米。如果我有很多单位,我会考虑课程的数量......根据单位名称,有工厂返回米的因素是否是个好主意?单位不会有课程吗?

4 个答案:

答案 0 :(得分:2)

嗯,我会使用enum而不是DistanceUnit类,因为它们没有不同的实例。 您可以将值设置为枚举like here

然后调用enum.getValue()而不是unit.getMetresFactor()。 它也有点令人困惑,是以米为单位的mValue值还是在DistanceUnit中,如果以米为单位,你必须有

double factor =  unit.getMetresFactor();

有 好的,现在有任何转换功能支持:

import java.util.HashMap;
import java.util.Map;


public abstract class MeasureConverter {

public abstract double valueToBasic(double value);

public abstract double basictoValue(double basic);

/**
 *
 */
public static Map<String, MeasureConverter> converters;

public static Map<String, MeasureConverter> getConverters() {
    if (converters == null) {
        converters = new HashMap<String, MeasureConverter>();

        converters.put("kilo", new MeasureConverter() {

            @Override
            public double valueToBasic(double value) {
                return value * 1000;
            }

            @Override
            public double basictoValue(double basic) {
                return basic / 0.001;
            }
        });

        // taking the basic temperature value in kelvines
        converters.put("kelvine", new MeasureConverter() {

            @Override
            public double valueToBasic(double value) {
                return value;
            }

            @Override
            public double basictoValue(double basic) {
                return basic;
            }
        });

        converters.put("celsius", new MeasureConverter() {

            @Override
            public double valueToBasic(double value) {
                return value + 273.15;
            }

            @Override
            public double basictoValue(double basic) {
                return basic - 273.15;
            }
        });

        converters.put("faren", new MeasureConverter() {

            @Override
            public double valueToBasic(double value) {
                return value * 1.8 - 459.67 ; // or whatever is there?
            }

            @Override
            public double basictoValue(double basic) {
                return (basic + 459.67  ) / 1.8;// or whatever is there?
            }
        });

    }

    return converters;
}

}

然后:

import java.util.Objects;


public class MeasurePattern {

double value;
String name;

public MeasurePattern(double value, String name) {
    this.value = value;
    this.name = name;
}

@Override
public String toString() {
    return "MeasurePattern{" + "value=" + value + ", name=" + name + '}';
}

@Override
public int hashCode() {
    int hash = 7;
    hash = 29 * hash + (int) (Double.doubleToLongBits(this.value) ^ (Double.doubleToLongBits(this.value) >>> 32));
    hash = 29 * hash + Objects.hashCode(this.name);
    return hash;
}

@Override
public boolean equals(Object obj) {
    if (obj == null) {
        return false;
    }
    if (getClass() != obj.getClass()) {
        return false;
    }
    final MeasurePattern other = (MeasurePattern) obj;
    if (Double.doubleToLongBits(this.value) != Double.doubleToLongBits(other.value)) {
        return false;
    }
    if (!Objects.equals(this.name, other.name)) {
        return false;
    }
    return true;
}

public MeasurePattern convertTo(String converter) {

    MeasureConverter mycon = MeasureConverter.getConverters().get(name);
    MeasureConverter hiscon = MeasureConverter.getConverters().get(converter);

    double basic = mycon.valueToBasic(value);
    double hisValue = hiscon.basictoValue(basic);
    return new MeasurePattern(hisValue, converter);

}

public static void main(String[] args) {
    //trying temperatures;

    MeasurePattern temp = new MeasurePattern(10, "celsius");

    MeasurePattern kelvine = temp.convertTo("kelvine");
    MeasurePattern faren = kelvine.convertTo("faren");
    MeasurePattern cels = faren.convertTo("celsius");

    System.out.println("kelvine = " + kelvine);
    System.out.println("faren = " + faren);
    System.out.println("cels = " + cels);

}

}

输出:

kelvine = MeasurePattern{value=283.15, name=kelvine}
faren = MeasurePattern{value=412.67777777777775, name=faren}
cels = MeasurePattern{value=9.999999999999943, name=celsius}

答案 1 :(得分:1)

您可以将其模拟为java.util.concurrent.TimeUnit作为枚举。 E.g。

public enum DistanceUnit {

  KILOMETER {
    @Override
    protected double conversionFactor(DistanceUnit toDistanceUnit) {
      switch (toDistanceUnit) {
        case KILOMETER:
          return 1;
        case MILE:
          return 0.621371;
        default:
          throw new UnsupportedOperationException(toDistanceUnit + " is not supported");
      }
    }
  },

  MILE {
    @Override
    protected double conversionFactor(DistanceUnit toDistanceUnit) {
      switch (toDistanceUnit) {
        case KILOMETER:
          return 1.60934;
        case MILE:
          return 1;
        default:
          throw new UnsupportedOperationException(toDistanceUnit + " is not supported");
      }
    }
  };

  public double toDistance(double value, DistanceUnit targetDistance) {
    return value * conversionFactor(targetDistance);
  }

  protected abstract double conversionFactor(DistanceUnit toDistanceUnit);

}

将您的Distance课程更改为

public class Distance {
    double mValue;
    DistanceUnit mUnit;

    public Distance(double value, DistanceUnit unit){
        this.mValue = value;
        this.mUnit = unit;
    }

    public Distance toUnit(DistanceUnit unit){
        double newValue = mUnit.toDistance(mValue, unit);
        Distance distance = new Distance(newValue, unit);
        return distance;
    }

    @Override
    public String toString(){
        return String.valueOf(mValue);
        }
}

并且客户端代码看起来非常清晰

public class Main {

  public static void main(String[] args) {
    Distance kilometers = new Distance(265.35, DistanceUnit.KILOMETER);
    Distance miles = kilometers.toUnit(DistanceUnit.MILE);
    System.out.println(miles);
  }
}

将输出

164.88079485000003

答案 2 :(得分:0)

我认为你应该使用&#34;策略&#34;图案。

界面:

 public interface DistanceUnit {
     double getDistance(int metres);
 }

英寸课程:

public class Inch implements DistanceUnit {
    @Override
    public double getDistance(int metres) {
       return meters*39; //do conversion here
    }
}

公里级:

public class Kilometres implements DistanceUnit {
    @Override
    public double getDistance(int metres) {
        return meters/1000; //do conversion here
    }
}

然后:

List<DistanceUnit> distanceList = new ArrayList<>();
distanceList.add(new Inch());
distanceList.add(new Kilometres());


for (DistanceUnit item : distanceList) {
    System.out.println(item.getDistance(1000));
}

如果我理解你,我认为这是一个简单而干净的解决方案。

您可以按照此模型进行其他单位之间的转换。

答案 3 :(得分:0)

Java约定不使用m(ember)前缀(但是说this.限定),并且在java中非常重视约定(例如,与C ++相反)。

toString错过了单位。

JScience提供更多,以不同单位计算的能力,m/s²,等等。你的课是一个很好的抽象。但是在更广泛的背景下,你可能想要进行数学运算,单位的幂(上面s为-2)。

首先看一下自己的使用方法:

(只是垃圾:)

U speedUnit = U.of(Distance::km, Time::h.up(-1));
double timeInS = U.mile(40).div(speedunit(30)).in(U.m);