我曾多次看到有人提到这一点,但我不清楚这是什么意思。你什么时候,为什么要这么做?
我知道接口是做什么的,但我不清楚这一点的事实使我认为我错过了正确使用它们。
如果你要这样做
IInterface classRef = new ObjectWhatever()
你可以使用任何实现IInterface的类吗?你什么时候需要这样做?我能想到的唯一一件事是,如果你有一个方法,你不确定什么对象将被传递,除了它实现IInterface。我不知道你需要多久做一次。
另外,如何编写一个方法来接受实现接口的对象呢?这可能吗?
问:——……“你能使用任何实现接口的类吗?”
A:是的。
问:——……“你什么时候需要这样做?”
答:-每次你都需要一个实现接口的类。
注意:我们不能实例化一个没有被类实现的接口- True。
为什么?
因为接口只有方法原型,没有定义(只有函数名,没有它们的逻辑)
AnIntf anInst = new class();
//我们可以这样做,只有当类实现AnIntf。
// anInst将有类引用。
注意:现在我们可以理解如果Bclass和Cclass实现相同的Dintf会发生什么。
Dintf bInst = new Bclass();
// now we could call all Dintf functions implemented (defined) in Bclass.
Dintf cInst = new Cclass();
// now we could call all Dintf functions implemented (defined) in Cclass.
我们拥有:相同的接口原型(接口中的函数名),调用不同的实现。
参考书目:
原型——维基百科
为一个界面编码是一种哲学,而不是特定的语言结构或设计模式——它指导你为了创建更好的软件系统而遵循正确的步骤顺序(例如,更有弹性,更可测试,更可伸缩,更可扩展,以及其他良好的特性)。
它的实际意思是:
===
在跳到实现和编码(如何)之前,先想想是什么:
你的系统中应该有哪些黑箱,
每个盒子的职责是什么?
每个“客户端”(即其他盒子,第三方“盒子”,甚至人类)应该与它(每个盒子的API)通信的方式是什么?
在你理清上面的问题之后,继续执行这些方框(HOW)。
首先考虑什么是盒子,什么是它的API,这将引导开发人员提炼盒子的职责,并为自己和未来的开发人员标记它的公开细节(“API”)和隐藏细节(“实现细节”)之间的区别,这是一个非常重要的区别。
一个直接且容易注意到的收获是,团队可以在不影响总体架构的情况下更改和改进实现。它还使系统更具可测试性(它与TDD方法配合得很好)。
= = =
除了我上面提到的特质,你还可以在这个方向上节省很多时间。
微服务和DDD,如果做得正确,是“编码到接口”的很好的例子,然而这个概念在每个模式中获胜,从巨石到“无服务器”,从BE到FE,从面向对象到功能性,等等....
我强烈推荐这种方法用于软件工程(我基本上相信它在其他领域也完全有意义)。
我曾经给学生的一个具体例子是他们应该写作
List myList = new ArrayList(); // programming to the List interface
而不是
ArrayList myList = new ArrayList(); // this is bad
在一个短程序中,它们看起来完全相同,但如果在程序中继续使用myList 100次,就会开始看到区别。第一个声明确保只调用myList上由List接口定义的方法(因此没有ArrayList特定的方法)。如果您以这种方式对接口进行了编程,那么稍后您就可以确定您确实需要
List myList = new TreeList();
你只需要在这一点上修改代码。您已经知道,其余的代码不会因为更改实现而被破坏,因为您对接口进行了编程。
当您谈论方法参数和返回值时,好处甚至更明显(我认为)。举个例子:
public ArrayList doSomething(HashMap map);
该方法声明将您绑定到两个具体实现(ArrayList和HashMap)。一旦从其他代码调用该方法,对这些类型的任何更改都可能意味着您将不得不更改调用代码。最好是根据接口进行编程。
public List doSomething(Map map);
现在,不管您返回什么样的List,或者作为参数传入什么样的Map。在doSomething方法中所做的更改不会强制您更改调用代码。
此外,我在这里看到了很多很好的解释性答案,所以我想在这里给出我的观点,包括一些我在使用这种方法时注意到的额外信息。
单元测试
在过去的两年里,我写了一个业余项目,但我没有为它写单元测试。在写了大约50K行代码后,我发现编写单元测试是非常必要的。
我没有使用接口(或者很少使用)……当我做第一个单元测试时,我发现它很复杂。为什么?
因为我必须创建大量的类实例,用作类变量和/或参数的输入。所以测试看起来更像集成测试(必须制作一个完整的类“框架”,因为所有的类都捆绑在一起)。
界面恐惧
所以我决定使用接口。我担心的是我必须在所有地方(在所有使用的类中)多次实现所有功能。在某种程度上,这是正确的,然而,通过使用继承,它可以减少很多。
接口和继承的组合
我发现这个组合很好用。我举一个非常简单的例子。
public interface IPricable
{
int Price { get; }
}
public interface ICar : IPricable
public abstract class Article
{
public int Price { get { return ... } }
}
public class Car : Article, ICar
{
// Price does not need to be defined here
}
这样就不需要复制代码,同时仍然有使用汽车作为接口(ICar)的好处。