我曾多次看到有人提到这一点,但我不清楚这是什么意思。你什么时候,为什么要这么做?

我知道接口是做什么的,但我不清楚这一点的事实使我认为我错过了正确使用它们。

如果你要这样做

IInterface classRef = new ObjectWhatever()

你可以使用任何实现IInterface的类吗?你什么时候需要这样做?我能想到的唯一一件事是,如果你有一个方法,你不确定什么对象将被传递,除了它实现IInterface。我不知道你需要多久做一次。

另外,如何编写一个方法来接受实现接口的对象呢?这可能吗?


当前回答

为一个界面编码是一种哲学,而不是特定的语言结构或设计模式——它指导你为了创建更好的软件系统而遵循正确的步骤顺序(例如,更有弹性,更可测试,更可伸缩,更可扩展,以及其他良好的特性)。

它的实际意思是:

===

在跳到实现和编码(如何)之前,先想想是什么:

你的系统中应该有哪些黑箱, 每个盒子的职责是什么? 每个“客户端”(即其他盒子,第三方“盒子”,甚至人类)应该与它(每个盒子的API)通信的方式是什么?

在你理清上面的问题之后,继续执行这些方框(HOW)。

首先考虑什么是盒子,什么是它的API,这将引导开发人员提炼盒子的职责,并为自己和未来的开发人员标记它的公开细节(“API”)和隐藏细节(“实现细节”)之间的区别,这是一个非常重要的区别。

一个直接且容易注意到的收获是,团队可以在不影响总体架构的情况下更改和改进实现。它还使系统更具可测试性(它与TDD方法配合得很好)。

= = = 除了我上面提到的特质,你还可以在这个方向上节省很多时间。

微服务和DDD,如果做得正确,是“编码到接口”的很好的例子,然而这个概念在每个模式中获胜,从巨石到“无服务器”,从BE到FE,从面向对象到功能性,等等....

我强烈推荐这种方法用于软件工程(我基本上相信它在其他领域也完全有意义)。

其他回答

Using interfaces is a key factor in making your code easily testable in addition to removing unnecessary couplings between your classes. By creating an interface that defines the operations on your class, you allow classes that want to use that functionality the ability to use it without depending on your implementing class directly. If later on you decide to change and use a different implementation, you need only change the part of the code where the implementation is instantiated. The rest of the code need not change because it depends on the interface, not the implementing class.

This is very useful in creating unit tests. In the class under test you have it depend on the interface and inject an instance of the interface into the class (or a factory that allows it to build instances of the interface as needed) via the constructor or a property settor. The class uses the provided (or created) interface in its methods. When you go to write your tests, you can mock or fake the interface and provide an interface that responds with data configured in your unit test. You can do this because your class under test deals only with the interface, not your concrete implementation. Any class implementing the interface, including your mock or fake class, will do.

编辑:下面是一篇文章的链接,其中Erich Gamma讨论了他的引用,“面向接口编程,而不是面向实现编程。”

http://www.artima.com/lejava/articles/designprinciples.html

面向接口编程非常棒,它促进了松耦合。正如@lassevk提到的,控制反转是一个很好的应用。

此外,研究SOLID原则。这是一个系列视频

它通过硬编码(强耦合示例),然后查看接口,最后进展到IoC/DI工具(NInject)。

为一个界面编码是一种哲学,而不是特定的语言结构或设计模式——它指导你为了创建更好的软件系统而遵循正确的步骤顺序(例如,更有弹性,更可测试,更可伸缩,更可扩展,以及其他良好的特性)。

它的实际意思是:

===

在跳到实现和编码(如何)之前,先想想是什么:

你的系统中应该有哪些黑箱, 每个盒子的职责是什么? 每个“客户端”(即其他盒子,第三方“盒子”,甚至人类)应该与它(每个盒子的API)通信的方式是什么?

在你理清上面的问题之后,继续执行这些方框(HOW)。

首先考虑什么是盒子,什么是它的API,这将引导开发人员提炼盒子的职责,并为自己和未来的开发人员标记它的公开细节(“API”)和隐藏细节(“实现细节”)之间的区别,这是一个非常重要的区别。

一个直接且容易注意到的收获是,团队可以在不影响总体架构的情况下更改和改进实现。它还使系统更具可测试性(它与TDD方法配合得很好)。

= = = 除了我上面提到的特质,你还可以在这个方向上节省很多时间。

微服务和DDD,如果做得正确,是“编码到接口”的很好的例子,然而这个概念在每个模式中获胜,从巨石到“无服务器”,从BE到FE,从面向对象到功能性,等等....

我强烈推荐这种方法用于软件工程(我基本上相信它在其他领域也完全有意义)。

假设你有一个名为“Zebra”的产品,可以通过插件进行扩展。它通过在某个目录中搜索dll来查找插件。它加载所有这些dll,并使用反射来查找实现IZebraPlugin的任何类,然后调用该接口的方法来与插件通信。

这使得它完全独立于任何特定的插件类——它不关心类是什么。它只关心它们是否满足接口规范。

接口是这样定义可扩展性点的一种方式。与接口对话的代码是松散耦合的——事实上,它与任何其他特定的代码根本不耦合。它可以与多年后由从未见过原始开发人员的人编写的插件进行互操作。

你可以使用一个带有虚函数的基类——所有的插件都是从基类派生的。但这有很大的限制,因为一个类只能有一个基类,而它可以实现任意数量的接口。

我是这个问题的后来者,但我想在这里提到的是,在GoF (Gang of Four)设计模式一书中,“为接口编程,而不是为实现编程”这一行有一些很好的讨论。

它在第18页说:

针对接口编程,而不是针对实现编程 不要将变量声明为特定具体类的实例。相反,只提交到由抽象类定义的接口。你会发现这是本书设计模式的一个共同主题。

在此之上,它是这样开始的:

仅根据抽象类定义的接口来操作对象有两个好处: 客户端仍然不知道他们使用的对象的具体类型,只要对象遵循客户端期望的接口。 客户端仍然不知道实现这些对象的类。客户端只知道定义接口的抽象类。

So in other words, don't write it your classes so that it has a quack() method for ducks, and then a bark() method for dogs, because they are too specific for a particular implementation of a class (or subclass). Instead, write the method using names that are general enough to be used in the base class, such as giveSound() or move(), so that they can be used for ducks, dogs, or even cars, and then the client of your classes can just say .giveSound() rather than thinking about whether to use quack() or bark() or even determine the type before issuing the correct message to be sent to the object.