我刚看完YouTube上的Google清洁代码视频(请参阅link,第一篇文章),关于从代码中删除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
语句?
答案 0 :(得分:8)
在看到其中一些(技术上正确的)响应后,这里只是一个注意事项,只是摆脱一个If语句不应该是你唯一的目标,目标应该是使你的代码可扩展,可维护和简单,如果这意味着摆脱if语句,很好,但它本身不应该是一个目标。
在您提供的代码示例中,并且在不了解您的应用程序的更多信息的情况下,假设您不会延长过去测试的空值,我认为If(或者甚至可能是三元组)更易于维护解决方案是完全坦率的。
答案 1 :(得分:4)
您实际上将实施类似策略模式的内容。首先定义一个超类,让我们称之为AbstractTableInfoCommand。这个类可能是抽象的,但必须指定一个名为runTableInfoCommand()的方法。
然后,您可以定义几个子类,每个子类都实现runTableInfoCommand()方法。你的类,具有Number属性的类,将具有一个类型为AbstractTableInfoCommand的新属性(让我们称之为tableInfoCommand),它将被实例化为AbstractTableInfoCommand的一个具体子类。
代码将是:
public int Number
{
get
{
return this.tableInfoCommand.runTableInfoCommand();
}
}
所以你可以创建一个NullTableInfoCommand和SomeOtherTableInfoCommand等。优点是如果你有一个新的条件来返回tableinfocommand然后你添加一个新的类而不是编辑这个代码。
话虽如此,并非所有情况都适合这种模式。所以它会产生更多可扩展的代码,但是如果你处于不需要可扩展性的情况下,那就太过分了。
答案 2 :(得分:1)
我觉得你的代码非常好。可读。简单。 (我希望它有效)。 如果此代码块重复n次,则需要通过应用Extract方法删除重复。
您指出的重构意味着替换重复出现的开关案例..如果您的示例中的语句不简单。 Replace Conditional With Polymorphism。记住“最简单的工作方式”......这意味着完成工作所需的最少数量的类和方法。
答案 3 :(得分: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。
答案 4 :(得分: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
这里的代码可能如下所示:
public int Number
{
get
{
string returnValue;
internalTableClass myinstance(parameters);
return Convert.ToInt32(this.RunTableInfoCommand(myinstance.giveInternalTableIdentifier, TableInfoEnum.TAB_INFO_NUM));
}
}
并且internalTableClass的代码将是微不足道的(使用:internalAbstractTableClass,以及两个继承自它的类,一个给出名称,另一个给出数字)
答案 5 :(得分: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的抽象类中继承RunTableCommand(int)的通用实现,并且是FooWithNum和FooWithName的基类。
答案 6 :(得分:0)
我会这样重构:
table = this.internalTableNumber == null ? internalTableName : internalTableNumber.Value;
return Convert.ToInt32(this.RunTableInfoCommand(table, TableInfoEnum.TAB_INFO_NUM));
喜欢三元运算符。
答案 7 :(得分:0)
在这种情况下,进行涉及多态的重构可能是过度的(取决于您可能从多态性中获得的其他优势)。在这种情况下,添加一个简单的重载,该重载封装了要调用RunTableInfoCommand()
的逻辑。
由于RunTableInfoCommand()
,internalTableNumber
和internalTableName
似乎都是同一个类的成员,因此一个好的,简单的重构可能是添加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
答案 8 :(得分:0)
我如何重构这些特定的if语句以使用多态?
好吧......我不会。您不需要使用多态来消除if语句。注意if语句本身并不“坏”,if语句是由表示选择引起的
(internalTableNumber == null) ==>
internalTableName else internalTableNumber.Value
这种关联意味着缺少类,即拥有一个拥有internalTableName和internalTablenumber的InternalTable类更有意义,因为这两个值与空检查规则密切相关。
假设InternalTable实例名为iTable,那么重构的重构代码将如下所示:
public int Number
{
get
{
return iTable.RunTableInfoCommand(TableInfoEnum.TAB_INFO_NUM);
}
}
这将在InternalTable类中封装null-check。修改原始RunTableInfoCommand签名是可选的。
请注意,不“将if语句替换为多态”,但它确实通过封装消除了消费类中的if语句。