Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/21.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# 如何重构此条件以使用多态性?_C#_.net_Polymorphism - Fatal编程技术网

C# 如何重构此条件以使用多态性?

C# 如何重构此条件以使用多态性?,c#,.net,polymorphism,C#,.net,Polymorphism,我刚看完YouTube上的Google clean code视频(参见,第一篇文章),内容是关于从代码中删除if语句并改用多态性 看了视频后,我看了看视频前我正在写的一些代码,注意到一些地方我可以使用这种方法,主要是在一些地方,同样的逻辑被多次实现。举个例子: 我有一些这样的代码 public int Number { get { string returnValue; if (this.internalTableNumber == null)

我刚看完YouTube上的Google clean code视频(参见,第一篇文章),内容是关于从代码中删除
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所做的并不重要,但最重要的是,我有许多属性与的属性完全相同,如果Statement唯一改变的是TableInfoEnum


我想知道是否有人能帮助我重构这个函数,使它仍然做同样的事情,但是没有任何<代码> < < /Cord>语句> < /P> < P>我可能会考虑将返回值的取取到可在运行时注入的另一个类。

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

  public int Number 
  { 
    get 
    {
      return this.ValueFetcher.GetValue<int>(/* parameters to identify the value to fetch */);
    }
  }
}
公共类的东西
{
public IValueFetcher ValueFetcher{get;set;}
公共整数
{ 
得到
{
返回此.ValueFetcher.GetValue(/*参数,用于标识要获取的值*/);
}
}
}
这将处理大量重复的代码,并减少对接口值源的依赖


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

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

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

代码将是:

public int Number
    {
        get
        {

            return this.tableInfoCommand.runTableInfoCommand();
        }
    }
因此,您可以创建NullTableInfoCommand和其他tableinfocommand等。其优点是,如果您有返回tableinfocommand的新条件,则可以添加新类,而不是编辑此代码


尽管如此,并非所有情况都适合这种模式。因此,它使代码更具可扩展性,但如果您所处的环境不要求可扩展性,则可能会有点过头。

我假设
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,以及从中继承的两个类,一个给出名称,另一个给出数字)

编辑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是您拥有表号时使用的类。然后,我将用一个数字方法编写每个类——实际上,我还将编写一个接口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,然后只使用接口方法,而不考虑实现。这可以很容易地扩展为在一个抽象类中继承RunTableCommand(int)的通用实现,该抽象类继承IFoo,并且是FooWithNum和FooWithName的基类。

我将重构它,从而:

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

喜欢三元运算符。

在看到这些(技术上正确的)响应之后,这里只是一个警告,去掉If语句不应该是你的唯一目标,目标应该是使代码可扩展、可维护和简单,如果这意味着去掉If语句,那就太好了,但它本身不应该是一个目标


在您给出的代码示例中,在不了解您的应用程序的情况下,假设您不打算对空值的测试进行太多的扩展,我认为If(或者甚至三元)是更易于维护的解决方案,可以非常坦率地说。

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


您指出的重构是为了替换反复出现的切换情况。。如果您的示例中的语句。记住“最简单有效的事情”。。这意味着完成这项工作所需的类和方法的数量最少。

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

由于
RunTableInfoCommand()
internalTableNumber
internalTableName
似乎都是memb
private string RunTableInfoCommand( TableInfoEnum infoEnum)
{
    if (this.internalTableNumber == null) {
        return this.RunTableInfoCommand( internalTableName, infoEnum);
    }

    return this.RunTableInfoCommand( internalTableNumber.Value, infoEnum);
}
returnValue = this.RunTableInfoCommand( TableInfoEnum.TAB_INFO_NUM);    
                                        // or whatever enum is appropriate
(internalTableNumber == null) ==> 
    internalTableName else internalTableNumber.Value
public int Number
{
    get
    {
        return iTable.RunTableInfoCommand(TableInfoEnum.TAB_INFO_NUM);
    }
}