Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/272.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
C# 重构if-else if-else_C#_Design Patterns_If Statement_Refactoring_Solid Principles - Fatal编程技术网

C# 重构if-else if-else

C# 重构if-else if-else,c#,design-patterns,if-statement,refactoring,solid-principles,C#,Design Patterns,If Statement,Refactoring,Solid Principles,我有下面的代码示例 if(object.Time > 0 && <= 499) { rate = .75m } else if(object.Time >= 500 && <= 999) { rate = .85m } else if(object.Time >= 1000) { rate = 1.00m } else { rate = 0m; } if(object.Time>0&&=500

我有下面的代码示例

if(object.Time > 0 && <= 499)
{
     rate = .75m
}
else if(object.Time >= 500 && <= 999)
{
     rate = .85m
}
else if(object.Time >= 1000)
{
     rate = 1.00m
}
else
{
     rate = 0m;
}
if(object.Time>0&&=500&&=1000)
{
速率=1.00m
}
其他的
{
速率=0米;
}
我的问题是,我可以使用什么样的设计模式来改进它

编辑:为了更清楚一点,您在这里看到的代码是目前存在于策略模式实现中的代码。我们有3种类型的计算,其中2种有3种不同的“速率”,可以根据您在下面看到的时间使用。我曾想过为每种利率创建一个策略实现,但随后我会移动确定策略使用的逻辑,并将其弄得一团糟


谢谢

if-else
条件下,您可以选择一种格式而不是设计模式

一般来说,如果你有很多条件,我更喜欢
if
而不是很多嵌套的
if-else
,你可以选择这样的条件

if(condition1){
   return x;    // or some operation
}

if(condition 2){
   return y;   // or some operation
}

return default; // if none of the case is satisfied.

我不认为这是一个反模式的问题,对于代码度量也是oki。If不是嵌套的,也不是很复杂!。 但您可能会做得更好,例如使用开关,或者创建自己的类,该类包含IsAgeBiggerThanMax()等属性


交换机更新:

        var range = (time - 1) / 499;
        switch (range)
        {
            case 0:  // 1..499
                rate = 0.75;
                break;
            case 1: // 500.. 999 
                rate = 0.85;
                break;
            default:
                rate = 0;
                if (time == 1000)
                {
                    rate = 1.0;
                }
                break;
        }

测试是一个哲学问题,我们不知道这个函数是什么,它在做什么。也许它可以从外部100%测试

有一种不太出名的模式叫做“规则模式”

其思想是,所有内容都被提取到一个对象中,让它处理自己的工作。您将为每个规则定义每个类,这些规则是您定义的条件语句,例如(object.Time>0&&0&&date.Something=500&&date.Something 0&&500&&1000) { 速率=1.00m } 其他的 { 速率=0米; } 现在是,

private List<IRules>_rules = new List<IRules>();
public SomeConstructor()
{
    _rules.Add(new RuleNumberOne());
    _rules.Add(new RuleNumberTwo());
}

public void DoSomething()
{

    Oobject date = new Oobject();

    foreach(var rule in this._rules)
    {
        Decimal rate = rule.Execute(date);
    }
}
private List_rules=new List();
公共构造函数()
{
_添加(新的RuleNumberOne());
_添加(新规则编号wo());
}
公共无效剂量测定法()
{
Oobject日期=新Oobject();
foreach(本规则中的var规则)
{
十进制率=规则。执行(日期);
}
}
这里的想法是,一旦得到嵌套的if条件,就很难读取条件语句,开发人员也很难进行任何更改。因此,它将每个单独规则的逻辑及其效果分离到自己的类中,该类遵循规则单一责任模式

一些注意事项是 1.)只读 2.)明确的顺序 3.)依赖关系 4)优先权 5)持久性

再次,考虑当条件复杂度越来越大,应用程序的要求保证时,使用规则模式。


如果您不想让它返回十进制或其他内容,您可以对其进行自定义,但想法就在这里。

如果您有大量的“如果”,或者如果您想将此信息放在设置文件中,那么我建议您创建一个类来存储此信息

Class
    FromTime
    ToTime
    Value

values.Add(New Class(0, 499, .75));
values.Add(New Class(500, 999, .85));
values.Add(New Class(1000, 9999, 1));
然后循环集合中的每个项目

if(object.Time >= curItem.FromTime && object.Time <= curItem.ToTime)
    rate = curItem.Value;

if(object.Time>=curItem.FromTime&&object.Time=curItem.FromTime | | curItem.FromTime=-1)&&(object.Time只需在每个if中进行一次比较,并使用值从上到下进行比较:

if (Object.Time >= 1000)
    rate = 1.0;
else
    if (Object.Time >= 500)
        rate = 0.85;
    else
        if (Object.Time > 0)
            rate = 0.75;
        else
            rate = 0;

您只需要检查范围的一个端点,另一个端点由您在代码中的实际位置暗示,因为前面的条件为false

if (obj.Time <= 0) {
    rate = 0.00m;
}

// At this point, obj.Time already must be >= 0, because the test
// to see if it was <= 0 returned false.
else if (obj.Time < 500) {
    rate = 0.75m;
}

// And at this point, obj.Time already must be >= 500.
else if (obj.Time < 1000) { 
    rate = 0.85m;
}

else {
    rate = 1.00m;
}
if(obj.Time=0,因为测试
//看看它是否等于500。
如果(对象时间<1000){
速率=0.85米;
}
否则{
速率=1.00m;
}

出于可读性和性能方面的原因,最好是将量表中更常见的一端设置为您首先检查的一端。但这两种方式都适用。

如果您真的在寻找设计模式,我会选择责任链模式

基本上,你的“链接”试图处理输入。如果它无法处理它,它将沿着链传递,直到其他链接可以处理它。如果你有一些接口,你也可以定义一个接口,以便在单元测试中轻松模拟

因此,每个链接都将继承这个抽象类:

public abstract class Link
{
    private Link nextLink;

    public void SetSuccessor(Link next)
    {
        nextLink = next;
    }

    public virtual decimal Execute(int time)
    {
        if (nextLink != null)
        {
            return nextLink.Execute(time);
        }
        return 0;
    }
}
然后使用规则创建每个链接:

public class FirstLink : Link
{
    public override decimal Execute(int time)
    {
        if (time > 0 && time <= 499)
        {
            return .75m;
        }

        return base.Execute(time);
    }
}

public class SecondLink : Link
{
    public override decimal Execute(int time)
    {
        if (time > 500 && time <= 999)
        {
            return .85m;
        }

        return base.Execute(time);
    }
}

public class ThirdLink : Link
{
    public override decimal Execute(int time)
    {
        if (time >= 1000)
        {
            return 1.00m;
        }

        return base.Execute(time);
    }
}
你所要做的就是用一个干净的电话来呼叫连锁店:

var result = chain.Execute(object.Time);

我真的很喜欢Leo Lorenzo Luis的解决方案。 但我不会返回利率,而是让规则来处理它。 这将从根本上尊重美国 还有

另外,当一个类“询问”包含在另一个类中的值时,您可以将其标识为名为的
气味

也就是说:我会做两件事来完善Leo Lorenzo的解决方案:

  • 调用右
    规则
    ,而不调用
    for循环
  • 在其关联的
    规则
    类中执行请求的行为
  • 为了做到这一点,我们必须将
    规则
    类与其时间范围进行映射,这样就可以直接访问它们,而不必通过循环进行迭代。您需要实现自己的映射对象(或列表对象,或集合),重载
    []操作符
    及其
    添加
    函数

    因此,您可以在地图中添加规则,例如:

    ranges.Add(0,500).AddRule(rule1);
    ranges.Add(500,1000).AddRule(rule2);
    etc.. 
    
    您可以在上面看到,存在一个对象
    范围
    ,该对象可以关联一个对象
    规则
    。因此,您最终可以为同一
    范围
    添加多个规则

    然后,你这样称呼它:

    ranges[Object.time].Execute(Object);
    
    使用地图:

    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));
    
    测试失败。尽管调用了
    RuleNumberOne
    并返回了一个非零值,但随后调用了
    RuleNumberWO
    并返回了零以覆盖正确的值

    为了复制if..else..else逻辑,它需要能够短路

    这里有一个快速修复方法:更改接口的
    Execute
    方法以返回
    bool
    以指示是否
    var result = chain.Execute(object.Time);
    
    ranges.Add(0,500).AddRule(rule1);
    ranges.Add(500,1000).AddRule(rule2);
    etc.. 
    
    ranges[Object.time].Execute(Object);
    
    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));
    
    [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));
    }
    
    [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; }
        }
    }