2016-05-11 4 views
0

Я искал некоторые хорошие шаблоны, чтобы иметь возможность выразить расстояние в разных единицах. Я нашел Martin Fowler article about quantities и я запрограммировал что-то вроде:Это хороший способ реализовать расстояние с различными единицами измерения

Вот Расстояние класс (я думаю, что это не necessery, чтобы сделать его аннотация):

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» для счетчика. Я думаю о количестве занятий, если бы у меня было много единиц ... Хорошо ли иметь фабрику, которая возвращает коэффициент метров на основе названия единицы? Тогда не будет классов для единиц?

+1

Вместо того, чтобы изобретать колесо, у вас думал о usi ng библиотека, подобная [JScience] (http://jscience.org/), которая уже существует? –

+0

Я ожидал, что где-то есть что-то вроде JScience, но я не слышал об этом, я его не нашел. Я это проверю. Но мой вопрос был также, если это хороший способ сделать это, как я. Я не очень опытный программист, поэтому каждое хорошее слово из меня стоит много. –

+0

@ Tim Biegeleisen как будто он может использовать свои дистанции. Нет смысла изобретать колесо, если он проще и быстрее, чем использовать существующий плохой –

ответ

2

Хм, я бы использовал перечисление вместо классов DistanceUnit, потому что их нет. Вы можете установить значение для перечисления like here

и затем вызвать enum.getValue() вместо unit.getMetresFactor(). Кроме того, это немного сбивает с толку, это значение MVALUE в метрах или в DistanceUnit-х, если в метрах, вы должны иметь

double factor = unit.getMetresFactor(); 

там Ok и теперь любой функции Convertion поддержки:

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

, но если кто-то захочет добавить новый блок, ему придется редактировать класс enum? Итак, мне нужно будет добавить все возможные единицы в перечисление? –

+0

Да, я думаю, что это не так неправильно с редактированием перечисления. В практическом мире. Если вы не хотите никаких модификаций ... Ahh, вы можете создать ** карту ** единиц измерения доли, например HashMap dims; где вы помещаете как dims.put («километр», 0,001); dims.put ("дюйм", <безотносительно); , то кому-то нужно просто добавить новую запись на карту, лучше создать новый почти пустой класс. –

+0

ОК. Благодарю. Понимаю. Я все еще думаю о том, что Тим Бигелейзен показал. Разве не лучше использовать его, а не переписывать? Это действительно так медленно lib? –

0

Я думаю, вы должны использовать "Стратегия" рисунок.

Интерфейс:

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)); 
} 

Если я понимаю вас, я думаю, что это простое и чистое решение.

Вы можете следить за этой моделью для преобразования между другими устройствами.

+0

Я не думаю, что это хорошее решение. Если бы я хотел конвертировать километры в дюймы? Поэтому он не автоматизирован. –

+0

Вы можете иметь разные иерархии и поддерживать их независимо. Я думаю, что всегда лучше делать больше классов и писать меньше кода. – Alex

+0

PS: Если в будущем будет изобретено новое расстояние, вы должны добавить только свою новую иерархию конверсий. Разделение - это хорошо. – Alex

0

Java-соглашение не использует префикс m (ember) (но скажем this.), и соглашение принято совершенно серьезно в java (в отличие от C++, например).

toString не хватает единицы измерения.

JScience предлагает больше возможностей для расчета в разных единицах, m/s² и так далее. Ваш класс - хорошая абстракция. Но в более широком контексте вы, вероятно, захотите иметь математические операции, полномочия единиц (-2 для s выше).

Посмотрите на свои собственные идеи использования первых:

(просто мусор :)

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

Да, я вижу, я сделал это, потому что я был немного смущен, когда я их умножал. Поэтому я вижу, что в моей проблеме мне нужно будет программировать многое, что уже реализовано. –

+1

Иначе это было бы не очень интересно? Возможно, немного поиграйте с примерами JScience и посмотрите, сможете ли вы сделать это лучше. У них есть _aliases_ (если я правильно помню), как Hz для s-1 и таких. –

1

Вы можете реализовать его аналогово java.util.concurrent.TimeUnit как перечисление. Например.

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 
Смежные вопросы