8

我刚刚在 YouTube 上观看了关于从代码中删除语句并改用多态性 的 Google 干净代码视频(参见链接,第一篇文章) 。if

看完视频看了看视频之前写的一些代码,发现有些地方可以使用这种方法,主要是同一种逻辑多次实现的地方。举个例子:

我有一些这样的代码。

public int Number
{
    get
    {
        string returnValue;
        if (this.internalTableNumber == null)
             returnValue = this.RunTableInfoCommand(internalTableName,
                                                    TableInfoEnum.TAB_INFO_NUM);
        else
             returnValue = this.RunTableInfoCommand(internalTableNumber.Value,
                                                    TableInfoEnum.TAB_INFO_NUM);
        return Convert.ToInt32(returnValue);
    }
}

RunTableInfoCommand 的作用并不重要,但主要是我有许多具有完全相同if语句的属性,唯一改变的是 TableInfoEnum。

我想知道是否有人可以帮助我重构它,以便它仍然做同样的事情但没有任何if声明?

4

9 回答 9

8

在看到其中一些(技术上正确的)响应后,这里只是一个警告,只是摆脱 If 语句不应该是您的唯一目标,目标应该是使您的代码可扩展、可维护和简单,如果这意味着摆脱if 语句,很好,但它本身不应该是一个目标。

在您提供的代码示例中,并且不了解您的应用程序的更多信息,并且假设您不会扩展太多过去的空值测试,我认为 If(甚至可能是三元)是更易于维护的解决方案非常坦率。

于 2008-12-11T04:08:42.947 回答
4

您实际上将实现类似策略模式的东西。首先定义一个超类,我们称之为 AbstractTableInfoCommand。此类可能是抽象的,但必须指定一个名为 runTableInfoCommand() 的方法。

然后,您可以定义几个子类,每个子类都实现 runTableInfoCommand() 方法。您的类,即具有 Number 属性的类,将具有 AbstractTableInfoCommand 类型的新属性(我们称之为 tableInfoCommand),它将被实例化为 AbstractTableInfoCommand 的具体子类之一。

然后代码将是:

public int Number
    {
        get
        {

            return this.tableInfoCommand.runTableInfoCommand();
        }
    }

因此,您可以创建 NullTableInfoCommand 和 SomeOtherTableInfoCommand 等。优点是,如果您有一些返回 tableinfocommand 的新条件,那么您可以添加一个新类而不是编辑此代码。

话虽如此,并非每种情况都一定适合这种模式。因此,它可以生成更多可扩展的代码,但如果您处于不需要可扩展性的情况下,它可能会有点矫枉过正。

于 2008-12-11T03:55:59.603 回答
1

我觉得你的代码非常好。可读。简单的。(我希望它有效)。如果您有此代码块重复 n 次,则需要通过应用 Extract 方法删除重复项。

您指出的重构旨在替换重复出现的 switch 案例.. 不像您的示例中那样简单的 if 语句。用多态替换条件。记住“最简单的工作”......这意味着完成工作所需的最少数量的类和方法。

于 2008-12-11T04:39:10.047 回答
0

我可能会考虑将返回值的获取传递给另一个可以在运行时注入的类。

public class Thing
{
  public IValueFetcher ValueFetcher { get; set; }

  public int Number 
  { 
    get 
    {
      return this.ValueFetcher.GetValue<int>(/* parameters to identify the value to fetch */);
    }
  }
}

这将处理大量重复代码并减少您对接口值来源的依赖。

我认为在某些时候您可能会有一个 if 语句,因为您仍然需要决定要调用哪个版本的 RunTableInfoCommand。

于 2008-12-11T03:54:57.420 回答
0

我假设internTableName并且InternalTableNumber是同一件事的某种价值。为什么不将它包装在一个类中并将该类的一个实例传递给this.RunTableInfoCommand这样:

public int Number
        {
            get
            {
                string returnValue;
                internalTableClass myinstance(parameters);
                return Convert.ToInt32(this.RunTableInfoCommand(myinstance, TableInfoEnum.TAB_INFO_NUM));
            }
        }

如果您仍然想使用多态,则可以在该类中通过重载例如giveInternalTableIdentifier返回数字或名称的 a 来实现

此处的代码可能如下所示:

public int Number
        {
            get
            {
                string returnValue;
                internalTableClass myinstance(parameters);
                return Convert.ToInt32(this.RunTableInfoCommand(myinstance.giveInternalTableIdentifier, TableInfoEnum.TAB_INFO_NUM));
            }
        }

并且 internalTableClass 的代码将很简单(使用:internalAbstractTableClass,以及从它继承的两个类,一个给出名称,另一个给出编号)

于 2008-12-11T04:00:49.653 回答
0

编辑 2我将如何真正解决问题。

我会让 InternalTableNumber 成为延迟加载的属性。如果它不可用,那么我会通过 InternalTableName 查找它。然后我总是只使用 InternalTableNumber 属性作为我的方法。

 private int? internalTableNumber;
 private int InternalTableNumber
 {
     get
     {
         if (!internalTableNumber.HasValue)
         {
             internalTableNumber = GetValueFromTableName( internalTableName );
         }
         return internalTableNumber;
     }
     set
     {
         internalTableNumber = value;
     }
 }

 public int Number
 {
     get
     {
        string value = this.RunTableInfoCommand(InternalTableNumber,
                                                TableInfoEnum.TAB_INFO_NUM);
        return Convert.ToInt32( value );
     }
 }

编辑 使用多态性...

假设您当前的类名为 Foo,那么我会将它重构为两个类,FooWithName 和 FooWithNumber。FooWithName 将是您在拥有表名时使用的类,而 FooWithNumber 将是在您拥有表号时使用的类。然后,我将使用 Number 方法编写每个类——实际上,我将编写一个接口 IFoo 以及每个接口,以便它们可以互换使用。

public interface IFoo
{
     int Number { get; }|

}

public class FooWithName : IFoo
{
     private string tableName;
     public FooWithName( string name )
     {
         this.tableName = name;
     }

     public int Number
     {
        get { return this.RunTableInfoCommand(this.tableName,
                                       TableInfoEnum.TAB_INFO_NUM);
     }

     ... rest of class, including RunTableInfoCommand(string,int);
}

public class FooWithNumber : IFoo
{
     private int tableNumber;
     public FooWithNumber( int number )
     {
         this.tableNumber = number;
     }

     public int Number
     {
        get { return this.RunTableInfoCommand(this.tableNumber,
                                       TableInfoEnum.TAB_INFO_NUM);
     }

     ... rest of class, including RunTableInfoCommand(int,int);
}

你会这样使用它:

IFoo foo;

if (tableNumber.HasValue)
{
    foo = new FooWithNumber( tableNumber.Value );
}
else
{
    foo = new FooWithName( tableName );
}

int number = foo.Number;

显然,除非您在现有类中有很多 if-then-else 构造,否则此解决方案实际上并没有对其进行太大改进。该解决方案使用多态创建 IFoo,然后只使用接口方法而不关心实现。这可以很容易地扩展为在继承 IFoo 并且是 FooWithNum 和 FooWithName 的基类的抽象类中继承 RunTableCommand( int ) 的通用实现。

于 2008-12-11T04:02:35.017 回答
0

我会这样重构它:

table = this.internalTableNumber == null ? internalTableName : internalTableNumber.Value;
return Convert.ToInt32(this.RunTableInfoCommand(table, TableInfoEnum.TAB_INFO_NUM));

喜欢三元运算符。

于 2008-12-11T04:03:32.630 回答
0

在这种情况下,进行涉及多态性的重构可能是矫枉过正(取决于您可能从多态性中获得哪些其他优势)。在这种情况下,添加一个简单的重载来封装RunTableInfoCommand()要调用的逻辑可能是有序的。

由于RunTableInfoCommand(),internalTableNumberinternalTableNameall 似乎是同一个类的成员,一个不错的、简单的重构可能是添加一个重载RunTableInfoCommand(),它只需要一个TableInfoEnum值并执行确定RunTableInfoCommand()需要调用哪个其他重载的逻辑:

private string RunTableInfoCommand( TableInfoEnum infoEnum)
{
    if (this.internalTableNumber == null) {
        return this.RunTableInfoCommand( internalTableName, infoEnum);
    }

    return this.RunTableInfoCommand( internalTableNumber.Value, infoEnum);
}

然后可以将具有相同if决策逻辑的众多呼叫站点折叠为:

returnValue = this.RunTableInfoCommand( TableInfoEnum.TAB_INFO_NUM);    
                                        // or whatever enum is appropriate
于 2008-12-11T04:49:13.567 回答
0

我将如何重构这些特定的 if 语句以使用多态性?

嗯......我不会。您不需要多态性来消除 if 语句。

请注意,if 语句本身并不“坏”,if 语句是由表示选择引起的,其中

(internalTableNumber == null) ==> 
    internalTableName else internalTableNumber.Value

这种关联意味着缺少类,即拥有一个拥有 internalTableName 和 internalTablenumber 的 InternalTable 类会更有意义,因为这两个值与 null 检查规则密切相关。

  • 这个新类可以提供一个执行 internalTableNumber == null 检查的 TableNumber 属性
  • 并且 RunTableInfoCommand 可以将 InternalTable 实例作为参数(但不是必须的,请参阅下一项)。
  • 但更好的是,新类应该具有执行整数转换的 RunTableInfoCommand 方法(可能是静态的)的外观。

假设 InternalTable 实例被命名为 iTable,那么重构后的代码将如下所示:

public int Number
{
    get
    {
        return iTable.RunTableInfoCommand(TableInfoEnum.TAB_INFO_NUM);
    }
}

This would encapsulate the null-check in the InternalTable class. Modifying the original RunTableInfoCommand signature is optional.

Note that this does not "replace the if statements with polymorphism", but it does eliminate the if statements from the consuming class via encapsulation.

于 2008-12-11T06:18:00.657 回答