接口的好例子

我在一家公司工作,有些人要求在我们的代码中使用接口(Visual Studio C#3.5)。

我想要求Iron Clad推理接口是必需的。 (我的目标是certificate接口是编程的正常部分。)

我不需要说服,我只需要一个好的论据来用于说服别人。

我正在寻找的那种论点是基于事实的,而不是基于比较的(即“因为.NET库使用它们”是基于比较的。)

因此,针对它们的论点是:如果一个类被正确设置(使用其公共和私有成员),那么接口只是额外的开销,因为使用该类的那些仅限于公共成员。 如果您需要一个由多于一个类实现的接口,那么只需设置inheritance/多态。

代码解耦。 通过编程接口,您可以使用接口与实现接口的代码分离代码。 这允许您更改实现,而无需使用它重构所有代码。 这与inheritance/多态相结合,允许您交替使用任何可能的实现。

模拟和unit testing。 当方法是虚拟的时,模拟框架最容易使用,默认情况下,这些方法是通过接口获得的。 这实际上是我创建界面的最大原因。

定义可能适用于允许它们互换使用的许多不同类的行为,即使类之间没有关系(定义的行为除外)。 例如,Horse和Bicycle类都可以使用Ride方法。 您可以定义定义Ride行为的接口IRideable,并且使用此行为的任何类都可以使用Horse或Bicycle对象,而不会强制它们之间的不自然inheritance。

因此,针对它们的论点是:如果一个类被正确设置(使用其公共和私有成员),那么接口只是额外的开销,因为使用该类的那些仅限于公共成员。 如果您需要一个由多于一个类实现的接口,那么只需设置inheritance/多态。

请考虑以下代码:

interface ICrushable { void Crush(); } public class Vehicle { } public class Animal { } public class Car : Vehicle, ICrushable { public void Crush() { Console.WriteLine( "Crrrrrassssh" ); } } public class Gorilla : Animal, ICrushable { public void Crush() { Console.WriteLine( "Sqqqquuuuish" ); } } 

建立一个将动物与车辆联系起来的阶级等级是否有任何意义,即使两者都被我的巨型破碎机压碎了? 没有。

除了在其他答案中解释的内容之外, interfaces允许您在.NET中模拟多重inheritance ,否则不允许。

有人说,唉

技术由两类人主导:那些了解他们不管理什么的人,以及管理他们不理解的人。

启用类的unit testing。

要有效地跟踪依赖关系(如果未检出并触摸接口,则只能更改类的语义)。

因为没有运行时开销。

启用依赖项注入。

…也许是因为它是’2009年,而不是70年代’,而现代语言设计师实际上对他们在做什么有一定的线索?

并不是应该在每个类接口抛出接口:只是那些对系统至关重要的接口,并且可能会经历重大的变化和/或扩展。

接口和抽象类模拟不同的东西。 当你有一个isA关系时,你从一个类派生,所以基类模拟一些具体的东西。 当类可以执行一组特定任务时,您可以实现一个接口。

想想可以序列化的东西,从设计/建模的角度来看,有一个名为Serializable的基类是没有意义的,因为说出一个可序列化的东西是没有意义的。 有一些实现Serializable接口的东西更有意义说’这是类可以做的事情,而不是类是什么’

接口不是“必需的”,这是一个设计决定。 我认为你需要说服自己,为什么,根据具体情况,使用接口是有益的,因为添加接口有一个开销。 另一方面,为了反对接口的争论,因为你可以“简单地”使用inheritance:inheritance有它的缺点,其中之一就是 – 至少在C#和Java中 – 你只能使用一次inheritance(单inheritance); 但第二个 – 也许更重要的是 – inheritance要求你不仅要理解父类的工作,还要理解所有的祖先类,这使得扩展更难,但也更脆弱,因为父类的变化’实现可以轻松打破子类。 这是GOF书中教给我们的“构成而不是inheritance”这一论点的关键。

您已经获得了一套指导方针,您的老板认为这些指导方针适合您的工作场所和问题领域。 因此,为了说服改变这些指导方针,并不是要certificate接口通常是一件好事,而是要certificate你在工作场所需要它们。

您如何certificate您在工作场所编写的代码中需要接口? 通过在实际代码库中找到一个位置(不是某些代码来自其他人的产品,当然也不是在某些玩具示例中有关Duck在IAnimal中实现makeNoise方法),其中基于接口的解决方案优于基于inheritance的解决方案。 向你的老板展示你所面临的问题,并询问是否有必要修改指南以适应这种情况。 这是一个受教育的时刻,每个人都在看同样的事实,而不是通过普遍性和猜测来打击对方。

该指南似乎是由于担心避免过度工程和过早泛化。 因此,如果你提出一个论点, 我们应该在这里有一个接口以防将来我们必须…… ,这是好心的,但对于你的老板,它引发了同样的过度工程警报铃声,这激发了准则首先。

等到它有一个很好的客观案例,这既适用于您在生产代码中使用的编程技术,也适用于您与经理开始争论的内容。

  • 测试驱动开发
  • unit testing

没有产生解耦代码的接口将是一个痛苦。 最佳实践是针对接口而不是具体实现进行编码。 界面看起来很垃圾,但一旦你发现好处,你总会使用它们。

  • 您可以实现多个接口。 您不能从多个类inheritance。
  • ..而已。 其他人关于代码解耦和测试驱动开发的观点并没有达到问题的关键,因为你也可以用抽象类来做这些事情。

接口允许您声明一个可以在多种类型( IEnumerable )之间共享的概念,同时允许每个类型具有自己的inheritance层次结构。

在这种情况下,我们所说的是“这个东西可以列举,但这不是它的唯一定义特征”。

接口允许您在定义实施者的function时做出必要的最少决策。 当您创建一个类而不是一个接口时,您已经声明您的概念是纯类的,不适用于结构。 在类中声明成员时,您还可以做出其他决策,例如可见性和虚拟性。

例如,您可以使用所有公共抽象成员创建一个抽象类,并且它非常接近于接口,但是您已经将该概念声明为在所有子类中都可以覆盖,而如果您不必做出该决定使用了一个界面。

它们也使unit testing更容易,但我不认为这是一个强有力的论据,因为你可以构建一个没有unit testing的系统(不推荐)。

如果您的商店正在执行自动化测试,那么接口对dependency injection非常有利,并且能够单独测试一个软件单元。

inheritance论证的问题在于你要么拥有一个巨大的上帝阶级,要么拥有如此深刻的层次结构,这将使你的头脑旋转。 最重要的是,你最终会得到一个你不需要或没有任何意义的课程。

我看到很多“没有多重inheritance”,虽然这是真的,但它可能不会让你的团队阶段化,因为你可以有多个级别的inheritance来获得他们想要的东西。

我想到了一个IDisposable实现。 您的团队会在Object类上放置一个Dispose方法,让它在系统中传播,无论它是否对某个对象有意义。

接口声明一个合同,任何实现它的对象都将遵守该合同。 这使得确保代码质量比尝试强制执行书面(非代码)或语言结构更容易,当一个类用接口引用装饰时,需求/契约是明确的,代码将不会编译直到你实现该界面完全和类型安全。

使用接口(这里列出)有许多其他很好的理由,但可能不会与管理产生共鸣,也不会产生良好的,老式的’质量’声明;)

好吧,我的第一反应是,如果你要解释为什么你需要接口,反正这是一场艰苦的战斗:)

话虽如此,除了上面提到的所有原因,接口是松散耦合编程的唯一方式,n层架构,你需要动态更新/替换组件等等 – 在个人经验中,这是一个太深奥的概念对于建筑团队的负责人而言,我们生活在dll地狱的结果 – 在.net世界中不变!

请提前原谅我的伪代码!

阅读SOLID原则。 使用接口的SOLID原则有几个原因。 接口允许您解除实现的依赖性。 您可以使用像StructureMap这样的工具更进一步,以使耦合真正消失。

您可能习惯的地方

 Widget widget1 = new Widget; 

这特别说明你想要创建一个新的Widget实例。 但是,如果你在另一个对象的方法中执行此操作,则现在说另一个对象直接依赖于Widget的使用。 所以我们可以这样说

 public class AnotherObject { public void SomeMethod(Widget widget1) { //..do something with widget1 } } 

我们仍然依赖于Widget的使用。 但至少这是可测试的,因为我们可以将Widget的实现注入SomeMethod。 现在,如果我们使用接口,我们可以进一步解耦。

 public class AnotherObject { public void SomeMethod(IWidget widget1) { //..do something with widget1 } } 

请注意,我们现在不需要Widget的特定实现,而是要求任何符合IWidget接口的内容。 这意味着可以注入任何东西,这意味着在日常使用代码时我们可以注入Widget的实际实现。 但这也意味着当我们想要测试这个代码时,我们可以注入假/模拟/存根(取决于你对这些术语的理解)并测试我们的代码。

但我们怎样才能更进一步。 通过使用StructureMap,我们可以进一步解耦这些代码。 使用最后一个代码示例我们的调用代码看起来像这样

 public class AnotherObject { public void SomeMethod(IWidget widget1) { //..do something with widget1 } } public class CallingObject { public void AnotherMethod() { IWidget widget1 = new Widget(); new AnotherObject().SomeMethod(widget1); } } 

正如您在上面的代码中看到的,我们通过传入符合IWidget的对象来删除SomeMethod中的依赖项。 但是在CallingObject()。AnotherMethod中我们仍然有依赖。 我们也可以使用StructureMap来删除这种依赖性!

 [PluginFamily("Default")] public interface IAnotherObject { ... } [PluginFamily("Default")] public interface ICallingObject { ... } [Pluggable("Default")] public class AnotherObject : IAnotherObject { private IWidget _widget; public AnotherObject(IWidget widget) { _widget = widget; } public void SomeMethod() { //..do something with _widget } } [Pluggable("Default")] public class CallingObject : ICallingObject { public void AnotherMethod() { ObjectFactory.GetInstance().SomeMethod(); } } 

请注意,在上面的代码中没有任何地方我们实例化AnotherObject的实际实现。 因为StructurMap的所有内容都是有线的,所以我们可以允许StructureMap根据代码运行的时间和地点传递适当的实现。 现在代码非常灵活,因为我们可以通过配置或编程方式指定我们想要使用的实现。 这种配置可以在运行中或作为构建过程的一部分等完成。但它不必在任何地方进行硬连接。

因为这不能解答有关接口案例的问题。

不过我建议让有问题的人阅读..

首先设计模式

– 李

我不明白它的额外开销。

接口提供灵活性,可管理的代码和可重用性。 编码到接口时,您不必担心正在使用的特定类的具体实现代码或逻辑。 你只是期待一个结果。 许多类对同一个特性事物(StreamWriter,StringWriter,XmlWriter)有不同的实现。你不需要担心它们如何实现写入,你只需要调用它。