我很少使用接口,并发现它们在其他代码中很常见。
另外,我很少在代码中创建子类和超类(同时创建自己的类)。
- 这是一件坏事吗?
- 您是否建议更改此样式?
- 这种风格有副作用吗?
- 这是因为我没有从事任何大型项目吗?
我很少使用接口,并发现它们在其他代码中很常见。
另外,我很少在代码中创建子类和超类(同时创建自己的类)。
Answers:
您可能要使用接口的原因有几个:
http://msdn.microsoft.com/zh-CN/library/3b5b8ezk(v=vs.80).aspx
接口就像编程中的其他任何东西。如果不需要它们,请不要使用它们。我已经看到它们被广泛地用作样式问题,但是如果您不需要接口提供的特殊属性和功能,那么“仅仅因为”它们并没有带来任何好处。
类继承和接口都有它们的位置。继承的意思是“是”,而接口提供的契约定义了“行为”。
我想说,经常使用接口根本不是一个坏习惯。我目前正在阅读Bill Wagner撰写的“有效的C#-50种改善C#的特定方法”。项目编号22指出,并用I引号“优先于继承定义和实现接口”。
通常,当我需要定义概念上相关的类型之间常见行为的特定实现时,我使用基类。我更经常使用接口。实际上,我通常在开始创建类时就从为类定义一个接口开始……即使最后我不编译该接口,我发现通过定义该类的公共API也可以帮助开始。从一开始就上课。如果我发现我有多个实现该接口的类,并且实现逻辑是相同的,则只有这样我才问自己,在这些类型之间实现一个公共基类是否有意义。
Bill Wagners的几句话引述...
所有派生类都立即包含该行为。向接口添加成员会破坏实现该接口的所有类。它们将不包含新方法,并且将不再编译。每个实施者必须更新该类型以包括新成员。在抽象基类和接口之间进行选择是一个问题,即随着时间的推移如何最好地支持抽象。接口是固定的:您将接口作为任何类型可以实现的一组功能的合同发布。基类可以随时间扩展。这些扩展成为每个派生类的一部分。可以混合使用这两种模型,以在支持多个接口的同时重用实现代码。” 它们将不包含新方法,并且将不再编译。每个实施者必须更新该类型以包括新成员。在抽象基类和接口之间进行选择是一个问题,即随着时间的推移如何最好地支持抽象。接口是固定的:您将接口作为任何类型可以实现的一组功能的合同发布。基类可以随时间扩展。这些扩展成为每个派生类的一部分。可以混合使用这两种模型,以在支持多个接口的同时重用实现代码。” 它们将不包含新方法,并且将不再编译。每个实施者必须更新该类型以包括新成员。在抽象基类和接口之间进行选择是一个问题,即随着时间的推移如何最好地支持抽象。接口是固定的:您将接口作为任何类型可以实现的一组功能的合同发布。基类可以随时间扩展。这些扩展成为每个派生类的一部分。可以混合使用这两种模型,以在支持多个接口的同时重用实现代码。” 您以任何类型可以实现的一组功能的合同形式发布接口。基类可以随时间扩展。这些扩展成为每个派生类的一部分。可以混合使用这两种模型,以在支持多个接口的同时重用实现代码。” 您以任何类型可以实现的一组功能的合同形式发布接口。基类可以随时间扩展。这些扩展成为每个派生类的一部分。两种模型可以混合使用,以在支持多个接口的同时重用实现代码。”
“与对基类类型进行编码相比,编码接口为其他开发人员提供了更大的灵活性。”
“使用接口为类定义API提供了更大的灵活性。”
“当您的类型将属性公开为类类型时,它会将整个接口公开给该类。使用接口,您可以选择仅公开希望客户端使用的方法和属性。”
“基类描述并实现了相关具体类型之间的常见行为。接口描述了不相关具体类型可以实现的功能的原子部分。两者都有其位置。类定义了您创建的类型。接口将这些类型的行为描述为功能块。如果您理解这些差异,那么您将创建更具表现力的设计,这些设计在面对变化时更具弹性。使用类层次结构来定义相关类型。使用跨这些类型实现的接口来公开功能。”
尚未提及的一件事是测试:在所有C#模拟库以及Java中的某些库中,除非类实现了接口,否则无法模拟它们。这导致许多遵循敏捷/ TDD实践的项目为每个类提供自己的界面。
有些人认为这是最佳做法,因为它“减少了耦合”,但我不同意-我认为这只是解决语言缺陷的一种解决方法。
我认为,当您有两个或多个类(抽象地以不同的方式执行“相同的事情”)时,最好使用接口。
例如,.NET框架有多个类,这些类的商店名单的东西,但他们都储存的东西以不同的方式。因此,有意义的是IList<T>
可以使用不同的方法来实现抽象接口。
如果希望2个以上的类在将来可互换或可替换时,也应使用它。如果将来出现一种新的存储列表方式AwesomeList<T>
,那么假设您IList<T>
在整个代码中都使用过,那么将其更改为使用AwesomeList<T>
将意味着仅更改几十行,而不是几百/千。
不,这一点都不坏。当且仅当具有一个公共接口的两个类需要在运行时互换时,才应使用显式接口。如果它们不需要可互换,则不要让它们继承。就这么简单。继承是脆弱的,应尽可能避免。如果可以避免使用它,或者改用泛型,则可以这样做。
问题在于,在C#和Java等具有弱编译时泛型的语言中,您最终可能违反DRY,因为除非所有类都继承自相同的基类,否则无法编写可以处理多个类的方法。不过,C#4 dynamic
可以解决这个问题。
事实是,继承就像全局变量一样-一旦添加它并且您的代码依赖于它,上帝就会帮助您取消继承。但是,您可以随时添加它,甚至可以通过使用排序包装器添加它而无需更改基类。
事情正在发生变化(MS Moles),但是我认为几乎只对接口进行编码是件好事的主要原因是它们易于模拟并且可以自然地适合IoC架构。
IMO,您应该只使用接口,或者在任何可能的情况下完全哑巴DAO。一旦您进入了这种思维定势,并且开始使用一个不会通过接口公开自身并通过具体对象来完成所有事情的库,那么我确实感到有些笨拙。
对于老式项目,人们使用接口来避免循环引用,因为从长远来看,循环引用可能会成为巨大的维护问题。
坏:
class B; // forward declaration
class A
{
B* b;
};
class B
{
A* a;
}
不错:
class PartOfClassB_AccessedByA
{
};
class A
{
PartOfClassB_AccessedByA* b;
};
class B : public PartOfClassB_AccessedByA
{
A* a;
}
通常使用单独的文件实现A,B,PartOfClassB_AccessedByA。
基于接口的编程有助于使代码更灵活且更易于测试。无需触摸客户端代码即可更改实现类[灵活性]。测试代码可以代替实际的基于oInterface的编程,这有助于使代码更灵活,更易于测试。无需触摸客户端代码即可更改实现类[灵活性]。在测试代码时,可以用模拟对象[Testability]替换实际对象。用模拟对象[Testability]替换对象。