Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/heroku/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何使用多个if语句提高方法的可读性和长度?_Java_If Statement_Design Patterns - Fatal编程技术网

Java 如何使用多个if语句提高方法的可读性和长度?

Java 如何使用多个if语句提高方法的可读性和长度?,java,if-statement,design-patterns,Java,If Statement,Design Patterns,我有一个195个ifs的方法。以下是一个简短的版本: private BigDecimal calculateTax(String country, BigDecimal amount) throws Exception { if(country.equals("POLAND")){ return new BigDecimal(0.23).multiply(amount); } else if(country.equals("AUSTRIA")) {

我有一个195个ifs的方法。以下是一个简短的版本:

private BigDecimal calculateTax(String country, BigDecimal amount) throws Exception {
    if(country.equals("POLAND")){
        return new BigDecimal(0.23).multiply(amount);
    }
    else if(country.equals("AUSTRIA")) {
        return new BigDecimal(0.20).multiply(amount);
    }
    else if(country.equals("CYPRUS")) {
        return new BigDecimal(0.19).multiply(amount);
    }
    else {
        throw new Exception("Country not supported");
    }
}
我可以将ifs更改为交换机:

private BigDecimal calculateTax(String country, BigDecimal amount) throws Exception {
    switch (country) {
        case "POLAND":
            return new BigDecimal(0.23).multiply(amount);
        case "AUSTRIA":
            return new BigDecimal(0.20).multiply(amount);
        case "CYPRUS":
            return new BigDecimal(0.19).multiply(amount);
        default:
            throw new Exception("Country not supported");
    }
}
但195个病例仍然很长。如何提高该方法的可读性和长度?在这种情况下,哪种模式最好?

创建一个将国家名称映射到其相应税率的
地图:

Map<String,Double> taxRates = new HashMap<> ();
taxRates.put("POLAND",0.23);
...

将数据放入XML文件或数据库中,然后使用它填充字典。这样,您可以轻松地更改数据,并将数据与应用程序逻辑分离。或者,只需在方法中执行SQL查询即可。

如果值是常量且不需要定期更改(我对此表示怀疑)。我将介绍一个使用Enum的静态元模型:

public enum CountryList {

    AUSTRIA(BigDecimal.valueOf(0.20)),
    CYPRUS(BigDecimal.valueOf(0.19)),
    POLAND(BigDecimal.valueOf(0.23));

    private final BigDecimal countryTax;

    CountryList(BigDecimal countryTax) {
        this.countryTax = countryTax;
    }

    public BigDecimal getCountryTax() {
        return countryTax;
    }

    public static BigDecimal countryTaxOf(String countryName) {
        CountryList country = Arrays.stream(CountryList.values())
                .filter(c -> c.name().equalsIgnoreCase(countryName))
                .findAny()
                .orElseThrow(() -> new IllegalArgumentException("Country is not found in the dictionary: " + countryName));

        return country.getCountryTax();
    }
}
然后

它可读,编译时安全,易于扩展,每个国家都有额外的元数据,而且样板文件更少。

不要这样做

现在,您的
calculateTax
方法就像一个容器,包含四种实际的
calculateTax
方法,三个国家各一种,无效案例各一种。按照这些思路所做的每一个其他方法都是这样的。按照这种模式,您将在许多方法中得到许多开关(检查相同的案例集),其中每个案例都包含案例的细节。但这正是多态性所做的,以一种更好的方式

像这样的模式强烈表明您没有利用面向对象,除非有其他原因,否则您肯定应该这样做。毕竟它是Java,这是一个很好的例子

创建一个类似于
TaxPolicy
的界面:

interface TaxPolicy {
    BigDecimal calculateTaxFor(BigDecimal saleAmount);
}
创建一个实现它的类:

class NationalSalesTaxPolicy implements TaxPolicy  {
    String countryName;
    BigDecimal salesTaxRate;

    // Insert constructor, getters, setters, etc. here

    BigDecimal calculateTaxFor(BigDecimal saleAmount) {
        return saleAmount.multiply(salesTaxRate);         
    }
}
然后,创建该类的对象,每个国家/地区创建一个对象。我们可以将此列表包装成一个新类,
NationalSalesTaxCalculator
,它将是我们计算任何国家销售税的一站式服务:

class NationalSalesTaxCalculator {
    static Map<String, NationalSalesTaxPolicy> SUPPORTED_COUNTRIES = Stream.of(
        new NationalSalesTaxPolicy("POLAND", "0.23"),
        new NationalSalesTaxPolicy("AUSTRIA", "0.20"),
        new NationalSalesTaxPolicy("CYPRUS", "0.19")
    ).collect(Collectors.toMap(NationalSalesTaxPolicy::getCountryName, c -> c));

    BigDecimal calculateTaxFor(String countryName, BigDecimal saleAmount) {
        NationalSalesTaxPolicy country = SUPPORTED_COUNTRIES.get(countryName);
        if (country == null) throw new UnsupportedOperationException("Country not supported");

        return country.calculateTaxFor(saleAmount);
    }
}
需要注意的一些关键好处:

  • 如果您添加了一个想要支持的新国家,您只需创建一个新对象。所有可能需要该对象的方法都会自动“做正确的事情”,而无需手动查找它们,以便添加新的if语句
  • 您可以根据需要调整功能。例如,在我居住的地方(加拿大安大略省),食品杂货不征收销售税。因此,我可以创建自己的
    NationalSalesTaxPolicy
    子类,该子类具有更细微的逻辑
  • 还有更大的改进空间。请注意,
    NationalSalesTaxCalculator.calculateTaxFor()
    包含一些特定于处理不受支持国家/地区的代码。如果我们向该类添加新操作,每个方法都需要相同的空检查和错误抛出

    相反,可以将其重构为使用。您实现了一个
    不受支持的taxpolicy
    ,该类 通过抛出异常来实现所有接口方法。像这样:

    class UnsuppoertedTaxPolicy implements TaxPolicy {
        public BigDecimal calculateTaxFor(BigDecimal saleAmount) {
            throw new UnsupportedOperationException("Country not supported");
        }
    }
    
    然后你就可以做了

    TaxPolicy countryTaxPolicy = Optional
        .ofNullable(SUPPORTED_COUNTRIES.get(countryName))
        .orElse(UNSUPPORTED_COUNTRY);
    return countryTaxPolicy.calculateTaxFor(saleAmount);
    
    这会将所有异常“集中”到一个位置,从而使它们更容易找到(因此更容易设置断点),更容易编辑(以防您想要迁移异常类型或更改消息),并且它会将其余代码分离,因此只需担心满意的情况

  • 这里有一个有效的演示:

    编辑:错过了@Alexander的答案;这可能有点过分,但他也抓住了要点:使用OOP。
    编辑2:实施@Luaan的建议

    我可能遗漏了一些明显的东西,在这么晚的阶段实现起来可能有点困难,但在我看来,这是一个面向对象编程的完美案例:

    您创建了一个
    Country
    类,该类包含与某个国家有关的所有内容,例如
    name
    calculateAX()
    方法等等,然后您的调用者(
    calculateTotalAmount()
    ,或其他什么)将调用
    Country.calculateAX(amount)
    ,而不是
    calculateAX(Country,amount)
    ,整个if/switch结构刚刚消失


    此外,当你增加对一个新国家的支持时(比如说,某个地方发生了另一场内战,一个国家分裂了),你只需在一个地方为这个新国家添加所有东西,而不是用巨大的
    if()
    链或
    switch()
    es…

    作为一个框架挑战来寻找无数方法

    195个案例并不太长if很清楚他们在做什么以及为什么,并且if每个案例中的代码都很小。是的,它很长,但它的可读性很好,因为你确切地知道它在做什么。长度并不一定意味着不可读


    当然,正如其他答案所说,这可能是一种代码气味,表明您没有正确使用OO。但就其本身而言,它很长,不是不可读的。

    如果您坚持使用
    switch
    :自JDK 12以来,
    switch
    表达式通过允许多个
    case
    标签和lambda样式的语法,有可能提高
    switch
    语句的可读性和长度:

    private BigDecimal calculateTax(字符串国家/地区,BigDecimal金额)引发异常{
    双费率=开关(国家/地区){
    案例“波兰”、“爱尔兰”->0.23;
    案例“奥地利”、“法国”->0.20;
    案例“塞浦路斯”、“德国”->0.19;
    默认->抛出新异常(“不支持的国家”);
    };
    返回新的BigDecimal(比率)。乘以(金额);
    }
    
    使用
    java.util.Map
    ?是的,地图或字典可能是你在这里的最佳选择。警告:避免将浮点数字与BigDecimal一起使用。BigDecimal(0.23)可能与BigDecimal(23)/BigDecimal(100)不同。后者是23%的正确代表。我投票结束这个问题,因为关于提高现有工作代码可读性的问题属于我@
    NationalSalesTaxCalculator calculator = new NationalSalesTaxCalculator();
    BigDecimal salesTax = calculator.calculateTaxFor("AUSTRIA", new BigDecimal("100"));
    System.out.println(salesTax);
    
    class UnsuppoertedTaxPolicy implements TaxPolicy {
        public BigDecimal calculateTaxFor(BigDecimal saleAmount) {
            throw new UnsupportedOperationException("Country not supported");
        }
    }
    
    TaxPolicy countryTaxPolicy = Optional
        .ofNullable(SUPPORTED_COUNTRIES.get(countryName))
        .orElse(UNSUPPORTED_COUNTRY);
    return countryTaxPolicy.calculateTaxFor(saleAmount);