我有如下所示的功能,它根据五个输入参数计算税额。 对于输入参数的每个组合,存在匹配的“条件”,其仅仅是返回语句,例如,如果输入参数是:
nationality == "German" and
netIncome == 45000 and
(birthDate >= 01.01.1950 && birthDate < 01.01.1960) and
childrenCount == 1
then tax value is 0.15%
如果我们考虑所有输入参数组合,if else语句会变得非常长并且嵌套。
public static decimal GetTax(int netIncome, string nationality, DateTime birthDate, int childrenCount, bool handicapped)
{
if (nationality == "DE")
{
if (handicapped)
{
// condition1
return 0;
}
if (birthDate >= new DateTime(1930, 1, 1) && birthDate < new DateTime(1936,1,1))
{
if (childrenCount == 1)
{
// condition2
return 0.05m;
}
else
{
// other conditions
}
}
else if(birthDate >= new DateTime(1950, 1, 1))
{
if (netIncome >= 30000 && netIncome < 40000)
{
if (childrenCount == 1)
{
// condition3
return 0.15m;
}
else if (childrenCount == 2 || childrenCount == 3)
{
// other conditions
return Convert.ToDecimal(netIncome * 0.10);
}
else
{
// other conditions
}
}
if (netIncome >= 40000 && netIncome < 50000)
{
if (childrenCount >= 1 && childrenCount < 3)
{
// other conditions
return Convert.ToDecimal(netIncome * 0.17);
}
else if (childrenCount >= 3 || childrenCount < 5)
{
// other conditions
return Convert.ToDecimal(netIncome * 0.16);
}
else
{
// other conditions
}
}
}
else
{
}
}
else if (nationality == "FR")
{
}
else if (nationality == "IT")
{
}
// just to satisfy the compiler
throw new Exception("The input parameters don't match any condition!");
}
问题是:如何简化上述if else语句,使其成为更容易理解且更易于维护的代码?
我提出了一个简单的(我希望)解决方案,我想提出一下:
(1)我创建了一个名为Condition的类,如下所示
private class Condition
{
private readonly decimal tax;
private readonly Predicate<string> nationalityPredicate;
private readonly Predicate<DateTime> birthDatePredicate;
private readonly Predicate<int> childrenCountPredicate;
private readonly Predicate<int> netIncomePredicate;
private readonly Predicate<bool> handicappedPredicate;
public Condition(decimal taxParameter, Predicate<string> nationality, Predicate<DateTime> birthDate, Predicate<int> netIncome, Predicate<int> childrenCount, Predicate<bool> handicapped)
{
this.tax = taxParameter;
this.nationalityPredicate = new Predicate<string>(nationality);
this.birthDatePredicate = new Predicate<DateTime>(birthDate);
this.netIncomePredicate = new Predicate<int>(netIncome);
this.childrenCountPredicate = new Predicate<int>(childrenCount);
this.handicappedPredicate = new Predicate<bool>(handicapped);
}
public bool IsTrue(string nationality, DateTime birthDate, int netIncome, int childrenCount, bool handicapped)
{
if (this.nationalityPredicate(nationality))
{
if (this.birthDatePredicate(birthDate))
{
if (this.netIncomePredicate(netIncome))
{
if (this.childrenCountPredicate(childrenCount))
{
if (this.handicappedPredicate(handicapped))
{
return true;
}
}
}
}
}
return false;
}
public decimal CalculateTax()
{
Debug.WriteLine(this.tax);
return this.tax;
}
}
此类对应于上面GetTax()函数中嵌套if else语句中的一个条件。例如当5个输入参数为Ethnic =“DE”,birthDate =“01.01.1955”,netIncome = 35,000,childrenCount = 1且disapped = false时,此条件的税率为0.15
此类用于分解长复杂的嵌套if else语句。 对于GetTax()函数中的每个return语句,我们使用一个Condition类instant。
(2)我创建了TaxCalculator类,它使用Condition类作为内部帮助类,如下所示
public class TaxCalculator
{
// List of conditions
private readonly List<Condition> conditions = new List<Condition>();
/// <summary>
/// Constructor. All the conditions are created and initialized here.
/// The conditions initialized in the constructor correspond to the nested if else statements in the GetTax() function.
/// </summary>
public TaxCalculator()
{
// This corresponds to Condition1 in the 'GetTax()' function
Condition condition = new Condition(
0,
nationality => nationality == "DE",
birthDate => true,
netIncome => true,
childrenCount => true,
handicapped => handicapped);
this.conditions.Add(condition);
// This corresponds to Condition2 in the 'GetTax()' function
condition = new Condition(
0.05m,
nationality => nationality == "DE",
birthDate => (birthDate >= new DateTime(1930, 1, 1)) && (birthDate < new DateTime(1936, 1, 1)),
netIncome => true,
childrenCount => childrenCount == 1,
handicapped => !handicapped);
this.conditions.Add(condition);
// This corresponds to Condition3 in the 'GetTax()' function
condition = new Condition(
0.15m,
nationality => nationality == "DE",
birthDate => birthDate >= new DateTime(1950, 1, 1),
netIncome => (netIncome >= 30000 && netIncome < 40000),
childrenCount => childrenCount == 1,
handicapped => !handicapped);
this.conditions.Add(condition);
}
/// <summary>
/// This function corresponds to the GetTax() function, it determines which Condition object corresponds
/// to the input parameters.
/// </summary>
public decimal CalculateTax(string nationality, DateTime birthDate, int netIncome, int childrenCount, bool handicapped)
{
// iterate the conditions
foreach (Condition c in this.conditions)
{
// check if the input parameters apply the current condition
if (c.IsTrue(nationality, birthDate, netIncome, childrenCount, handicapped))
{
// if yes, return the result of this condition
return c.CalculateTax();
}
}
// in case of no matching condition raise an exception.
throw new Exception("TaxCalculator class didn't match any condition for parameters...etc.");
}
}
TaxCalculator类对应于上面GetTax()函数中的长嵌套if else语句。 该类使用简单的lambda表达式在构造函数中创建Condition对象,每个Condition对象对应于GetTax()函数中的一个条件(标记为// Condition1,// Condition2和// Condition3)
(3)要计算给定输入参数集的税,我们执行以下操作:
TaxCalculator taxCalculator = new TaxCalculator();
// check for conditions
// condition3
decimal tax = taxCalculator.CalculateTax("DE", new DateTime(1985, 1, 1), 33000, 1, false);
// condition2
tax = taxCalculator.CalculateTax("DE", new DateTime(1933, 1, 1), 0, 1, false);
// condition1
tax = taxCalculator.CalculateTax("DE", new DateTime(1977, 1, 1), 0, 0, true);
问题:
答案 0 :(得分:0)
我会写第一个如果这样:
public bool IsTrue(string nationality, DateTime birthDate, int netIncome, int childrenCount, bool handicapped)
{
return (this.nationalityPredicate(nationality) &&
this.birthDatePredicate(birthDate) &&
this.netIncomePredicate(netIncome) &&
this.childrenCountPredicate(childrenCount) &&
this.handicappedPredicate(handicapped))
}
对于其余的它是一个经典的规则检查引擎,似乎很好。 Perhap只是反转最后一次抛出异常。这是一个选择问题,但例外不是正常的应用程序流程恕我直言。如果出现问题,你应该抛出异常,如果出现问题则不应该抛出异常。喜欢:
public decimal CalculateTax(string nationality, DateTime birthDate, int netIncome, int childrenCount, bool handicapped)
{
var result = this.condition.FirstOrDefault(c => c.IsTrue(nationality, birthDate, netIncome, childrenCount, handicapped));
if(result == null)
throw new Exception("TaxCalculator class didn't match any condition for parameters...etc.");
return result.CalculateTax();
}
Perhap只是要小心这个名字。 “是真的” ?什么是真的?从商业角度来看,甚至从技术角度来看,它意味着什么?我确信这对你来说非常清楚,但对于那些必须维护它的人来说,他将不得不看一下方法定义(在最好的情况下,如果不是整个规则引擎)来理解这个“ IsTrue“的意思是。至少你有评论,但一个好的方法名称,恕我直言,比数千行评论更好。