【问题标题】:How to extend this design for a generic converter in java?如何在 Java 中为通用转换器扩展这种设计?
【发布时间】:2010-06-08 12:09:39
【问题描述】:

这里是一小段货币转换器代码:

 public enum CurrencyType {

        DOLLAR(1),
        POUND(1.2),
        RUPEE(.25);

        private CurrencyType(double factor) {
            this.factor = factor;
        }
        private double factor; 

        public double getFactor() {
            return factor;
        }

    }

    public class Currency {

        public Currency(double value, CurrencyType type) {
            this.value = value;
            this.type = type;
        }

        private CurrencyType type;
        private double value;

        public CurrencyType getCurrencyType() {
            return type;
        }

        public double getCurrencyValue() {
            return value;
        }

        public void setCurrenctyValue(double value){
            this.value = value;
        }

    }

public class CurrencyConversion {


    public static Currency convert(Currency c1, Currency c2)
            throws Exception {
        if (c1 != null && c2 != null) {
            c2.setCurrenctyValue(c1.getCurrencyValue()
                    * c1.getCurrencyType().getFactor()
                    * c2.getCurrencyType().getFactor());
            return c2;
        } else
            throw new Exception();
    }


}

我想改进此代码,使其适用于不同的转换单位,例如:公斤到磅、英里到公里等。看起来像这样:

public class ConversionManager<T extends Convertible> {


    public T convert(T c1, T c2) 
    {
        //return null;
    }
}

感谢您的想法和建议。

【问题讨论】:

    标签: java design-patterns generics oop


    【解决方案1】:

    在我看来,这是面向对象的方式。请注意,您也可以对数据类型进行模板化,但我在这里只使用了双精度,因为它们可以表示任何标量单位。这提供了一种很好的方式来处理您所要求的不同度量单位之间的转换:距离、货币、质量等。

    public interface Metric {
        public String getReferenceUnit();
    }
    
    public class Distance implements Metric {
        public String getReferenceUnit() {
            return new String("Meters");
        }
    }
    
    public interface Units<T extends Metric> {
        public double getToReferenceFactor();
    }
    
    public class Kilometers implements Units<Distance> {
        public double getToReferenceFactor() {
            return 1000;
        }
    }
    
    static <T extends Metric> double convert(Units<T> from, Units<T> to) {
        return from.getToReferenceFactor() * (1.0 / to.getToReferenceFactor());
    }
    

    【讨论】:

    • 感谢您的回答。这里有一个问题:getReferenceUnit() 方法的意义何在?
    • 这个想法是每个度量都有一个参考单位,并且每个其他单位都根据参考单位定义自己。在您的原始示例中,参考单位是美元,其他货币表示为美元的转换因子。我在这里添加了 getReferenceUnit() 函数只是为了说明距离的参考单位是米,因此公里单位的实现将 1000 作为它的转换因子。
    【解决方案2】:

    不幸的是,Java 中的枚举不支持接口或超类(我想找出正确的原因)。

    您可以做的是在您的枚举类型(例如@Convertible)上声明一个注解,并使用反射访问它的因子。这很丑陋,你将无法在泛型中使用“T extends Convertible”表达式。

    【讨论】:

    • 是的,在使用泛型时不能扩展枚举这一事实很糟糕。使用自定义注释是我最后的手段。
    【解决方案3】:

    这是一个使用空心矩阵的通用转换器,它通过传递闭包来组合已知的转换器。在这种情况下,它会转换长度单位:

    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    public class Converter {
        private static final Map<String, Map<String, Double>> FACTOR_MAP
                = new HashMap<String, Map<String, Double>>();
    
        public static double convert(String from, String to, double value) {
            List<Step> stack = new ArrayList<Step>();
            stack.add(new Step(from, value));
            while (!stack.isEmpty()) {
                Step s = stack.remove(0);
                double val = s.value;
                String source = s.unit;
                if (source.equals(to)) {
                    return val;
                }
                Map<String, Double> map = FACTOR_MAP.get(source);
                if (map != null) {
                    for (Map.Entry<String,Double> entry: map.entrySet()) {
                        stack.add(new Step(entry.getKey(), val*entry.getValue()));
                    }
                }
            }
            throw new IllegalArgumentException("Cannot not convert from " + from
                    + " to " + to);
        }
    
        public static void registerFactor(String from, String to, double factor) {
            putFactor(from, to, factor);
            putFactor(to, from, 1.0/factor);
        }
    
        private static void putFactor(String from, String to, double factor) {
            Map<String, Double> map = FACTOR_MAP.get(from);
            if (map == null) {
                map = new HashMap<String, Double>();
                FACTOR_MAP.put(from, map);
            }
            map.put(to, factor);
        }
    
        static {
            registerFactor("cm", "mm", 10);
            registerFactor("in", "cm", 2.54);
            registerFactor("in", "pt", 72);
            registerFactor("pc", "pt", 12);
            registerFactor("px", "mm", 0.28);
        }
    
        private static class Step {
            private String unit;
            private double value;
    
            Step(String unit, double value) {
                this.unit = unit;
                this.value = value;
            }
        }
    }
    

    以下程序:

    public class Main {
        private static final String UNITS[] = {"cm", "mm", "in", "pt", "pc", "px"};
    
        public static void main(String[] args) {
            for (String unit1: UNITS) {
                for (String unit2: UNITS) {
                    System.out.println("1" + unit1 + " = "
                            + Converter.convert(unit1, unit2, 1) + unit2);
                }
            }
        }
    }
    

    给出以下结果:

    1cm = 1.0cm
    1cm = 10.0mm
    1cm = 0.39370078740157477in
    1cm = 28.346456692913385pt
    1cm = 2.3622047244094486pc
    1cm = 35.71428571428571px
    1mm = 0.1cm
    1mm = 1.0mm
    1mm = 0.03937007874015748in
    1mm = 2.8346456692913384pt
    1mm = 0.23622047244094485pc
    1mm = 3.571428571428571px
    1in = 2.54cm
    1in = 25.4mm
    1in = 1.0in
    1in = 72.0pt
    1in = 6.0pc
    1in = 90.71428571428571px
    1pt = 0.035277777777777776cm
    1pt = 0.35277777777777775mm
    1pt = 0.013888888888888888in
    1pt = 1.0pt
    1pt = 0.08333333333333333pc
    1pt = 1.2599206349206347px
    1pc = 0.42333333333333334cm
    1pc = 4.233333333333333mm
    1pc = 0.16666666666666666in
    1pc = 12.0pt
    1pc = 1.0pc
    1pc = 15.119047619047619px
    1px = 0.028000000000000004cm
    1px = 0.28mm
    1px = 0.011023622047244094in
    1px = 0.7937007874015748pt
    1px = 0.06614173228346457pc
    1px = 1.0px
    

    【讨论】:

    • 我喜欢这个。但是,您不认为现在所有这些字符串定义都存在依赖关系吗?是的,当然它们可以被外部化,但想法是让设计保持开放,这样您就不必修改现有代码来添加新行为。例如,如果我要添加一个新的“因子定义”,我想通过添加一个新类并将其作为参数发送到我的程序中进行转换。
    • 这只是一个例子。除了字符串(事件纯对象)之外,没有什么可以阻止您使用其他任何东西。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-28
    相关资源
    最近更新 更多