繁体   English   中英

重载与通用 arguments

[英]Overloads vs generic arguments

我有个问题。 在框架中,这主要是在 generics 出现之前编写的,你经常看到 function 有很多重载来处理不同类型的事情。

一个)

Parse(int data)
Parse(long data)
Parse(string data)
..etc

这似乎没问题,因为它有助于使每种方法的代码保持较小等等。 另一方面,现在使用 generics 您可以执行以下操作:

b)

Parse<T>(T data)

然后使用 typeof() 使用某种 ifs/switch 语句来尝试推断类型是什么以及如何处理它们。

什么是最佳做法? 或者有什么想法可以帮助我在 a) 和 b) 之间做出选择?

恕我直言,如果你需要if / switch语句,你应该更好地重载。 如果实现不依赖于具体类型仍然可以重用它,那么应该使用泛型。

所以作为一般规则:

  • 如果每种类型都有单独的实现,则重载
  • 如果您可以使用适用于所有可能类型的单个实现,请使用泛型。

代码嗅觉。

如果你有“ 某种if / switch ”,那就是一种只会尖叫多态的代码味道。 它表明泛型不是解决这个问题的方法。 当代码不依赖于您传递给它的具体类型时,应该使用泛型。

观看此Google Tech会谈视频:“ 清洁代码会谈 - 继承,多态,和测试 ”。 它具体针对您所谈论的内容。

您描述使用泛型导致一堆ifs / switch语句的模式是一种反模式。

对此的一个解决方案是实现策略模式,允许您使用泛型,但同时将Parse方法的关注点与知道如何处理每个不同的情况隔离开来。

例:

class SomeParser
{
    void Parse<T>(ParseStrategy<T> parseStrategy, T data)
    {
        //do some prep

        parseStrategy.Parse(data);

        //do something with the result;
    }
}

class ParseStrategy<T>
{
    abstract void Parse(T data);
}

class StringParser: ParseStrategy<String>
{
    override void Parse(String data)
    {
        //do your String parsing.
    }
}

class IntParser: ParseStrategy<int>
{
    override void Parse(int data)
    {
        //do your int parsing.
    }
}

//use it like so
[Test]
public void ParseTest()
{
    var someParser = new SomeParser();
    someParser.Parse(new StringParser(), "WHAT WILL THIS PARSE TO");
}

然后你就可以传递你开发的任何策略。 这样您就可以在多个类中正确隔离您的问题,而不是违反SRP(单一责任原则)。

这里有一个问题 - 如果你需要if / switch语句来使泛型工作,你可能会遇到更大的问题。 在这种情况下,很有可能泛型参数不能(正确地)用于每种类型,只是您正在处理的一组固定类型。 在这种情况下,您最好提供重载来单独处理特定类型。

这有很多好处:

  • 没有滥用的可能性 - 你是用户无法传递无效的参数
  • 您的API更清晰 - 非常明显哪些类型是合适的
  • 通用方法使用起来更复杂,对初学者来说并不那么明显
  • 泛型的使用表明任何类型都是有效的 - 它们确实应该适用于所有类型
  • 通用方法可能会更慢,性能更好

如果你的论证可以用于任何类型,那就不那么清楚了。 在这种情况下,我经常会考虑包含重载,以及类型的通用回退方法。 当您将“预期”类型传递给方法时,这会提高性能,但您仍然可以使用其他非预期类型。

虽然对此没有一刀切的规则,但是当特定类型无关时,应该使用泛型。 这并不是说您不能约束类型,但这种特定类型实际上并不重要。 在这种情况下,解析方法完全依赖于每种类型(并且不同)。 仿制药似乎不是一个好的解决方案。

我对这种情况的经验法则。 有重叠,也有微妙之处,但这会让你走上正轨:

  1. 重载方法/函数与处理类型有关,并且不对类型上可用的接口做出假设。

重载的 function 将专门处理传递给它的类型,以及它实现的任何接口。 示例 printObject(t) 可能需要从 't' 中提取属性并手动打印(例如print(t.name)cout << t.name;

  1. Generics 关心处理实现的接口,但不关心 object 的类型。

通用 function 将处理传递给它的任何类型,但期望实现特定接口(例如 printObject(t) 可能只调用 object 上的t.toString() ,假设它已实现)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM