何时应该封装泛型类型
本文关键字:泛型类型 封装 何时应 | 更新日期: 2023-09-27 18:07:14
我看到许多人建议您应该用更接近您的领域的类封装泛型类型,例如Steve和Nat在《成长中的面向对象软件,由测试指导》中建议:
我们的经验法则是,我们试图限制使用泛型传递类型[…]。特别是当应用于集合时,我们将其视为一种复制形式。这是一个暗示,有一个领域的概念,应该提取到一个类型。
一般来说,什么时候做这样的事情是一个好主意…
class PersonList : List<Person>
. .而不是直接使用List<Person>
?
您正在寻找的是Java或c#的typedef
运算符。
不幸的是,子类化方法不能很好地替代typedef
。
下面的文章"Java理论与实践:伪类型定义反模式"详细解释了原因。
我将逐字逐句地把那篇文章的结论抄在这里:
伪类型定义反模式的动机很简单足够了——开发人员想要一种定义更紧凑类型的方法标识符,特别是泛型使类型标识符更加详细的。问题是,这种习惯用法在使用它的代码和该代码的客户端,抑制了重用。你可能不喜欢泛型类型标识符的冗长,但这是这不是解决问题的办法。
我不同意这种观点。List<Person>
和PersonList
一样是一种类型。人员列表的领域概念也被封装在其中。如果你问我,我会说最好尽可能多地使用泛型,除非使用它们限制了你(见下文)或使代码难以理解。例如,一个在PersonList
上工作的函数将比在List<Person>
上工作的函数更难泛化,如果你甚至注意到它正在做一些通用的事情。
也就是说,特别是在Java中,泛型有一个限制,使它们不那么吸引人。由于类型擦除,当涉及到类型的静态方法/成员时,您不能完全利用泛型,并且您可能需要提取非泛型的特定类型,以便能够将其用于某些事情。底线是,在Java中,如果允许您保持类型安全,那么在许多情况下您确实需要提取特定类型。
因为让类型参数保持原样并不是真正的DRY。考虑这个类:
class Muffin {
List<string> _peopleWhoLikeMuffins = new List<string>();
public Muffin(List<string> peopleWhoLikeMuffins) {
_peopleWhoLikeMuffins = peopleWhoLikeMuffins);
}
public void AddMuffinLiker(string p) {
_peopleWhoLikeMuffins.Add(p);
}
}
它真的很短,只包含基本的功能,但是我不得不使用string
——泛型类型参数——四次。而且永远都是一样的。如果我决定稍后更改类型,我将不得不替换所有四个出现。
在现实世界中,我们谈论的是数百,而不是4。因此,总是将其封装并不是一件容易的事,但这绝对值得考虑。
现在,我的例子不是很好(不仅仅是因为愚蠢的名字),但是你明白了——你将有很多字段和变量声明和实例化,而且每次你都必须传递一个类型参数,这个参数在你的程序中总是相同的代码库,除非你的其他类也是泛型的。
这样做的另一个好处是,如果你需要向你的集合添加一些额外的状态/行为,你会少做很多工作。
话虽如此,我自己并不经常使用这种抽象。