【问题标题】:Re factoring a collection of IF statements that hold two arguments重构包含两个参数的 IF 语句集合
【发布时间】:2013-10-25 08:13:52
【问题描述】:

目前我有七个类似于以下代码的 if 语句:

if(hit.collider.gameObject.tag == "Colour1" && start_time > look_at_time)
{
    new_colour1.ChangeObjectMaterialColour(hit.collider.gameObject.renderer.material.color);

   var colums = GameObject.FindGameObjectsWithTag("column");
   foreach( GameObject c in colums)
    c.GetComponent<MeshRenderer>().materials[1].color = new_colour1.orignalMaterial;
}


else if(hit.collider.gameObject.tag == "Colour2" && start_time > look_at_time)
{
    new_colour2.ChangeObjectMaterialColour(hit.collider.gameObject.renderer.material.color);
    var colums = GameObject.FindGameObjectsWithTag("column");
    foreach( GameObject c in colums)
       c.GetComponent<MeshRenderer>().materials[1].color = new_colour2.orignalMaterial;
}

每个语句大约有 6 行代码,占用大量空间,阅读起来可能有点棘手。我想要做的是找到一种方法来重新考虑这一点,以便我的代码不那么笨重并且不占用太多空间。

我曾考虑将我的 if 语句集合更改为 switch 语句,但我发现 switch 语句无法像上面那样处理两个参数。如果有任何其他方式我可以重构我的代码但保持相同的功能,或者我是否坚持我的 if 语句集合?

编辑

已更新以包含我的 7 条语句中的两条。 请注意,我正在尝试减少我拥有的 IF 语句的数量,或者找到一种更聪明的方法来执行它们。我不想添加更多额外的代码行或 if 语句。

【问题讨论】:

  • 我觉得在这里问这个问题:codereview.stackexchange.com会更好
  • 向我们展示更多...这里没有很多工作要做。
  • 正如我所说,它实际上是上述 IF 语句中的 7 个,但我会编辑我的帖子,并将帖子交叉到代码审查(谢谢你,不知道有一个!)
  • 为了将来参考,在发布您的第二个 if 语句后,很明显它们并不相同,因此了解差异很重要。
  • 是的,你是对的。应该放置多个省略的 if 语句。

标签: c# refactoring


【解决方案1】:

老实说,我认为在您的情况下,最具可读性和可维护性的方法就是将这些 if 块中的逻辑移动到函数中:

  if(hit.collider.gameObject.tag == "Colour1" && start_time > look_at_time)
    {
        DoTheThing(new_colour1);
    }
if(hit.collider.gameObject.tag == "Colour2" && start_time > look_at_time)
    {
        DoTheThing(new_colour2);
    }
//and so on



 void DoTheThing(MyType newColour)
  {
 newColour.ChangeObjectMaterialColour(hit.collider.gameObject.renderer.material.color);
      var colums = GameObject.FindGameObjectsWithTag("column");
       foreach( GameObject c in colums)
        c.GetComponent<MeshRenderer>().materials[1].color = newColour.orignalMaterial;
    }

至于使用switch,你当然可以这样做:

switch(hit.collider.gameObject.tag)
{
  case "Colour1":
      if (start_time>look_at_time)
      {
        //do something
      }
      else if (start_time<look_at_time)
      {
        //something else
      }
      else
      {
         //they are equal, etc, etc
      }
    break;
case "Colour2":
  //and so on
}

或者如果所有 if 块在 start_timelook_at_time 之间进行相同的比较,那么您可以反转它:

enum TimeDifference
{
   LessThan,Equal,GreaterThan
}
//just one way to get a constant switchable value, not necessarily the best
var diff = TimeDifference.Equal;
if (start_time>look_at_time) {diff=TimeDifference.LessThan}
else if (start_time<look_at_time) {diff=TimeDifference.GreaterThan}

switch(diff)
{
  case TimeDifference.LessThan:
     switch(hit.collider.gameObject.tag)
     {
       case "Colour1":
         //do something for Colour1
        break;   
        case "Colour2":
         //do something for Colour2
        break;
      }//end switch Colour
     break;
  case TimeDifference.GreaterThan
    switch(hit.collider.gameObject.tag)
     {
       case "Colour1":
         //do something for Colour1
        break;   
        case "Colour2":
         //do something for Colour2
        break;
      }//end switch Colour
     break;  
   default:
    //nested switch for Colour in the == case, you get the idea.
}

尽管上述任何一个与几个格式良好且独立的if 块的可读性是非常值得商榷的。

另一种方法是使用Dictionary&lt;string,Action&lt;int,int&gt;&gt;:(假设start_timelook_at_time 都是ints 并且您的所有逻辑都以某种方式涉及它们)

var Actions = new Dictionary<string,Action<int,int>>();
Actions.Add("Colour1",(s,l)=>{
 if (start_time>look_at_time)
          {
            //do something
          }
          else if (start_time<look_at_time)
          {
            //something else
          }
          else
          {
             //they are equal, etc, etc
          }

});

然后你的主代码变成了 1 行:

Actions[hit.collider.gameObject.tag](start_time,look_at_time);

而且我确信还有更多方法可以重新排列代码。

【讨论】:

    【解决方案2】:

    感谢您更新代码,现在我们可以开始查看重复的内容了。例如,这两个代码块之间的唯一区别是 if 语句中的字符串"Color1""Color2",以及替换为new_colour2 的变量new_colour1

    从这里我建议如下:

    //This should be declared once - e.g. class level not method level.
    var colorDict = new Dictionary<string, [new_color_type]> 
                        {{"Colour1", new_colour1}, {"Colour2", new_colour2}};
    
    [new_color_type] newColor;
    if(start_time > look_at_time && colorDict.TryGetValue(
            hit.collider.gameObject.tag,
            out newColor))
    {
       newColor.ChangeObjectMaterialColour(
                                hit.collider.gameObject.renderer.material.color);
    
       var colums = GameObject.FindGameObjectsWithTag("column");
       foreach( GameObject c in colums)
          c.GetComponent<MeshRenderer>().materials[1].color =
                                                        newColor.orignalMaterial;
    }
    

    【讨论】:

    • 为什么使用Dictionary 而不是switch
    • @Jodrell,使用 switch 语句编写一个版本并比较代码 :) 我的猜测是 Dictionary 会更干净、更短和更简单。
    • 我做到了,stackoverflow.com/a/19585269/659190。我断言switch 显示出明显的意图,并将提供改进的运行时性能。虽然可以使用 Dictionary 节省一些输入,但这种简洁并不能转化为性能或可读性。
    • @Jodrell,正如您在回答中指出的那样,您引入了一个错误,并通过决定使用开关编写了不干净的代码。当你修复它时,我会删除我的 -1,但我想我已经表达了我的观点。
    • 在这里重新搜索后,stackoverflow.com/questions/1334087/…,我删除了我的答案,因为这是string 查找。
    猜你喜欢
    • 1970-01-01
    • 2016-02-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多