Using a map:
var map = new[]
{
new { Rule = (Func<Oobject, bool>) ( x => x.Time > 0 && x.Something <= 499 ),
Value = .75m },
new { Rule = (Func<Oobject, bool>) ( x => x.Time >= 500 && x.Something <= 999 ),
Value = .85m },
new { Rule = (Func<Oobject, bool>) ( x => true ),
Value = 0m }
};
var date = new Oobject { Time = 1, Something = 1 };
var rate = map.First(x => x.Rule(date) ).Value;
Assert.That( rate, Is.EqualTo(.75m));
I like the idea of @lll's Rules Pattern
answer but it has a flaw.
Consider the following test (NUnit):
[Test]
public void TestRulesUsingList()
var rules = new IRules[]{ new RuleNumberOne(), new RuleNumberTwo() };
var date = new Oobject { Time = 1, Something = 1 };
var rate = 0m;
foreach(var rule in rules)
rate = rule.Execute(date);
Assert.That( rate, Is.EqualTo(.75m));
}
The test fails. Although RuleNumberOne
was called and returned a non-zero value, RuleNumberTwo
was subsequently called and returned zero to overwrite the correct value.
In order to replicate the if..else..else logic, it need to be able to short circuit.
Here's a quick fix: change the interface's Execute
method to return a bool
to indicate whether to rule should fire and add a Value
property to get the rule's decimal
value. Also, add a defulat rule that alwasys evaluates true
and returns zero. Then change the implementation (test) to get the value of the first rule to evaluate true:
[Test]
public void TestRulesUsingList2()
{
var rules = new IRules[]{ new RuleNumberOne(), new RuleNumberTwo(),
new DefaultRule() };
var date = new Oobject { Time = 1, Something = 1 };
var rate = rules.First(x => x.Execute(date)).Value;
Assert.That( rate, Is.EqualTo(.75m));
}
public class Oobject
{
public int Time { get; set; }
public int Something { get; set; }
}
public interface IRules
{
bool Execute(Oobject date);
decimal Value { get; }
}
public class RuleNumberOne : IRules
{
public bool Execute(Oobject date)
{
return date.Time > 0 && date.Something <= 499;
}
public decimal Value
{
get { return .75m; }
}
}
public class RuleNumberTwo : IRules
{
public bool Execute(Oobject date)
{
return date.Time >= 500 && date.Something <= 999;
}
public decimal Value
{
get { return .85m; }
}
}
public class DefaultRule : IRules
{
public bool Execute(Oobject date)
{
return true;
}
public decimal Value
{
get { return 0; }
}
}