为了计算所得税,我尝试使用Enum策略方法使逻辑更简洁。但最后我不满意主要是因为我不得不为收入群体,限制和费率写不同的枚举。这是因为它们都是不同性质的常数。但由于这个原因,代码看起来并不紧凑,似乎缺乏封装。
我担心真的吗?请让我了解您的观点或更好的方法。
说,所得税组和相应的费率如下:
例如,适用于120万收入的所得税将是 200000的30%(1200000 - 1000000) 500000的+ 20%(1000000 - 500000) + 500,000的50%(500000 - 0) = 160000
代码:
package itcalc;
public class IncomeTaxCalculatorImpl {
private enum IncomeGroup {
ONE(Limit.GROUP_ONE_LIMIT) {
@Override
public double calculate(double income) {
return income * Rate.GROUP_ONE_RATE.value / 100;
}
},
TWO(Limit.GROUP_TWO_LIMIT) {
@Override
public double calculate(double income) {
return ((income - Limit.GROUP_ONE_LIMIT.maximum)
* Rate.GROUP_TWO_RATE.value / 100)
+ ONE.calculate(Limit.GROUP_ONE_LIMIT.maximum);
}
},
THREE(Limit.GROUP_THREE_LIMIT) {
@Override
public double calculate(double income) {
return ((income - Limit.GROUP_TWO_LIMIT.maximum)
* Rate.GROUP_THREE_RATE.value / 100)
+ TWO.calculate(Limit.GROUP_TWO_LIMIT.maximum
- Limit.GROUP_ONE_LIMIT.maximum)
+ ONE.calculate(Limit.GROUP_ONE_LIMIT.maximum);
}
};
private final Limit limit;
private enum Limit {
GROUP_ONE_LIMIT(0, 500000),
GROUP_TWO_LIMIT(500001, 1000000),
GROUP_THREE_LIMIT(1000001, Double.MAX_VALUE);
private final double minimum;
private final double maximum;
private Limit(double minimum, double maximum) {
this.minimum = minimum;
this.maximum = maximum;
}
}
private enum Rate {
GROUP_ONE_RATE(10), GROUP_TWO_RATE(20), GROUP_THREE_RATE(30);
private final double value;
private Rate(double value) {
this.value = value;
}
}
private IncomeGroup(Limit limit) {
this.limit = limit;
}
abstract double calculate(double income);
}
public double calculate(double income) {
for (IncomeGroup group : IncomeGroup.values()) {
if (income >= group.limit.minimum
&& income <= group.limit.maximum) {
return group.calculate(income);
}
}
throw new IllegalArgumentException("Invalid Income Value");
}
}
答案 0 :(得分:3)
Limit
和Rate
枚举是否会在其他地方重复使用?如果没有,你可以摆脱它们并修改IncomeGroup以获得限制和费率元素:
ONE(0,500000,10)
这样,你的枚举就会少一些,而且代码会更紧凑
答案 1 :(得分:1)
您不需要限制或费率类。您可以这样说,因为Rate,Limit和IncomeGroup之间存在一对一的对应关系 - 将它们所代表的值合并到IncomeGroup中,为您提供三个值:两个用于范围,一个用于百分比率。即使您在其他地方使用这些值,也可以执行此操作 - 只需将它们设置为枚举的公共最终字段。
顺便提一下,如果你使用的是双打,则范围的最小值应该是较低范围的最大值,而不是最大值加1。税表通常会给出这些价值,因为它们将收入汇总到最接近的美元。如果你不这样做,你可能会得到一个不属于这个范围的收入。
更为偶然的是,calculate()应该是静态的,这样可以省去计算出你所在的收入群体的麻烦。
答案 2 :(得分:1)
枚举对于使代码更具可读性很有用,但我想你的代码通常不会单独引用常量ONE,TWO或THREE,而是在执行计算时更有可能需要迭代所有的税组。
如果将税组常数与税收计算算法分开,代码可能会更清晰。使用对象数组,您可以编写:
public static final TaxGroup[] TAX_GROUPS = new TaxGroup[] {
new TaxGroup(1000000, 0.3),
new TaxGroup(500000, 0.2),
new TaxGroup(0, 0.1)
};
然后,您可以编写单个算法来计算税,而不是维护三个单独的算术副本:
int untaxed = income;
int tax = 0;
for(TaxGroup taxGroup : TAX_GROUPS) {
if(untaxed > taxGroup.lowerLimit) {
tax += (untaxed - taxGroup.lowerLimit) * taxGroup.rate;
untaxed = taxGroup.lowerLimit;
}
}
答案 3 :(得分:1)
正如我对税收计算所期望的那样,随着时间的推移,您可能需要考虑越来越多的事实。例如。可能需要从数据库加载定义收入组的范围,因为这些范围可能会发生变化。同样,特定收入群体的适用税率可能会发生变化。将这些范围硬编码到您的代码中并不是一个好主意!!
您可能希望了解规则引擎背后的想法,例如正向链接。 JBoss的Drools手册很好地描绘了如何使用规则引擎(以及思想在大多数规则引擎中传递)。
无论如何,我认为枚举策略模式适用于更简单的业务案例(例如打印给定对象,例如org.joda.time.format.DateTimeFormatter
)但不适用于可能需要聚合来自多个对象的事实来计算的更复杂案例结果(例如计算工资单,税收,核对账簿等)