Possible Duplicate:
What does it mean to “program to an interface”?
我不断地遇到这个词:
Program to an interface.
这到底是什么意思?一个真实的设计场景将受到高度赞赏。
- 另一个副本。我真的很喜欢这个问题,但它已经贴了很多次了…你试过使用搜索栏吗?这里有很多建议:stackoverflow.com/search?Q=编程+到+接口
简单地说,而不是用一种说
I depend on this specific class to do my work
你的写作方式
I depend on any class that does this stuff to do my work.
第一个示例表示一个类,该类依赖于特定的具体实现来完成其工作。从本质上来说,这不是很灵活。
第二个示例表示写入接口的类。它不关心您使用的具体对象,只关心它实现了某些行为。这使得类更加灵活,因为可以为它提供任意数量的具体实现来完成它的工作。
例如,一个特定的类可能需要执行一些日志记录。如果您编写的类依赖于一个textfilelogger,那么该类将永远被强制将其日志记录写到一个文本文件中。如果要更改日志记录的行为,必须更改类本身。类与其记录器紧密耦合。
但是,如果您编写的类依赖于一个ilogger接口,然后为该类提供一个textfilelogger,那么您将完成相同的事情,但会带来更灵活的额外好处。您可以随意提供任何其他类型的ilogger,而不必更改类本身。类及其记录器现在是松散耦合的,您的类更灵活。
- 简单。精确的。
- 不幸的是,回答不好。"程序到接口"就是这个意思。这并不意味着你不能依靠一个特定的类来完成这项工作。作为您的ILogger示例的一个反例,我提供了java.util.logging.Logger和org.apache.log4j.Logger类来考虑:这两个非常流行的日志API都没有实现单独的接口,直接从客户机代码使用。客户端代码,它仍然是"编程到接口",即Logger类的接口。
- Rog和Sy. 233;里约热内卢的要点不是特定的Java类是否提供了一个显式接口,而是您编写的代码是否依赖于特定的类而不是它所公开的接口。如果您编写的代码具体依赖于org.apache.log4j.Logger,那么就不是"编程到接口"。如果您编写的代码不关心提供的记录器是java.util.logging.Logger还是org.apache.log4j.Logger或任何其他公开公共接口的记录器实现,那么您正在编程到一个接口。
- @但人们总是在Logger类的接口上编写代码。当它们这样做时,它们正在编程到接口;当然,它们不会编程到类内的任何实现。我认为你对"接口"是什么感到困惑。与其他公共类一样,java.util.logging.Logger公共类具有一个隐式的公共接口,但仍然有一个接口,您可以将其编程到该接口。
- @Rog&233;Rio不,我确切地知道接口是什么,隐式和显式的。我们同意,如果您编写的代码只知道、关心和依赖于接口,那么我们就是黄金。"编程到实现"并不意味着"依赖于类的其他封装内部",您似乎在暗示这一点。这取决于任何特定的类(例如,创建一个需要‘java. U.L.Log.LoggER’实现的构造函数,这将不能用‘org.Apache .Log4j.LoggER’来工作)来完成您的工作。
- @好的,我们同意什么是接口。关于"程序到接口,而不是实现"(从GOF)的原理,我的解释是在"有效Java"2ED中解释的,项目52"通过它们的接口引用对象"。它是关于局部变量、参数类型和返回类型所使用的类型;它不禁止用new实例化实现类,也不要求每个类都有单独的接口。从书中可以看出:"如果不存在合适的接口,那么通过类而不是接口引用对象是完全合适的。"
- @罗格&233;里约热内卢很好,但这比我能接受的要宽松一点。如果您必须修改类以利用不同的依赖实现,即使它公开了相同的公共接口,那么您实际上并没有对该接口进行编程,是吗?换句话说,如果编写一个类来更新特定的记录器实现,并且在不更改类的代码的情况下,不能用相同的接口替换另一个记录器实现,那么就不会对接口进行编程。如果你能做到,那么你就是。
- @是的,我们可以同意。这种情况不应该出现在我们需要更改客户机代码以使用某个组件的不同实现的地方。但是请注意,我作为示例使用的Logger类没有这个问题;您不需要将客户机代码更改为,例如,将日志输出定向到其他地方。澄清对这一原则的理解是很重要的,因为在现实世界中,开发人员经常会创建许多(我看到有数百个项目)绝对没有意义和毫无价值的独立接口。
- 我喜欢"编程到接口"可能意味着什么的解释:softwarengineering.stackexchange.com/a/232366。
接口是相关方法的集合,它只包含这些方法的签名,而不是实际的实现。如果一个类实现了一个接口(class Car implements IDrivable),它必须为接口中定义的所有签名提供代码。
基本实例:你得把车和自行车分类。两者都实现接口idrivable:
1 2 3 4 5
| interface IDrivable
{
void accelerate();
void brake();
} |
1 2 3 4 5 6 7 8
| class Car implements IDrivable
{
void accelerate ()
{ System. out. println("Vroom"); }
void brake ()
{ System. out. println("Queeeeek");}
} |
1 2 3 4 5 6 7 8
| class Bike implements IDrivable
{
void accelerate ()
{ System. out. println("Rattle, Rattle, ..."); }
void brake ()
{ System. out. println("..."); }
} |
现在假设您有一个对象集合,这些对象都是"可驱动的"(它们的类都实现了IDrivable):
1 2 3 4 5 6 7
| List<IDrivable> vehicleList = new ArrayList<IDrivable>();
list.add(new Car());
list.add(new Car());
list.add(new Bike());
list.add(new Car());
list.add(new Bike());
list.add(new Bike()); |
如果现在要循环该集合,则可以依赖这样一个事实:集合中的每个对象都实现accelerate():
1 2 3 4
| for(IDrivable vehicle: vehicleList)
{
vehicle.accelerate(); //this could be a bike or a car, or anything that implements IDrivable
} |
通过调用该接口方法,您不需要编程到实现,而是编程到接口——一个确保调用目标实现特定功能的契约。使用继承可以实现相同的行为,但从公共基类派生会导致紧密耦合,使用接口可以避免这种耦合。
- 这个答案进入了我的脑海:)
- 很好地解释了接口本身,但它并不能真正回答这个问题。
多态性依赖于接口的编程,而不是实现。
仅根据抽象类定义的接口来操作对象有两个好处:
客户机仍然不知道他们使用的特定类型的对象,只要对象符合客户机期望的接口。
客户机仍然不知道实现这些对象的类。客户机只知道定义接口的抽象类。
这大大减少了子系统之间的实现依赖性,从而导致了编程到接口的原则。
有关此设计的进一步推理,请参见工厂方法模式。
资料来源:G.O.F.的"设计模式:可重用面向对象软件的元素"。
另请参见:工厂模式。何时使用工厂方法?
现实世界的例子是applenty。其中之一:
对于JDBC,您使用的是接口java.sql.Connection。但是,每个JDBC驱动程序都提供自己的Connection实现。您不必了解任何关于特定实现的信息,因为它符合Connection接口。
另一个是Java集合框架。有一个java.util.Collection接口,它定义了size、add和remove方法(等等)。所以您可以互换使用所有类型的集合。假设您有以下内容:
1 2 3
| public float calculateCoefficient (Collection collection ) {
return collection. size() * something / somethingElse ;
} |
以及其他两个调用此方法的方法。另一种方法使用LinkedList,因为它的目的更有效,而另一种方法使用TreeSet。
由于LinkedList和TreeSet都实现了Collection接口,所以只能使用一种方法来进行系数计算。无需复制代码。
这里有一个"程序到接口"—你不在乎size()方法的实现有多精确,你知道它应该返回集合的大小—也就是说,你已经编程到Collection接口,而不是LinkedList和TreeSet接口。
但我的建议是找到一个阅读-也许是一本书(例如,用Java思考)-在这个概念被详细解释。
每个对象都有一个公开的接口。一个集合有add、remove、At等,一个套接字可以有Send、Receive、Close等。
您实际上可以得到的每个对象都有这些接口的具体实现。
这两件事都是显而易见的,然而不那么明显的是…
您的代码不应该依赖于对象的实现细节,只依赖于它发布的接口。
如果你把它推向极端,你只需要对Collection等进行编码(而不是ArrayList)。实际上,只需确保在不破坏代码的情况下交换概念上相同的内容即可。
敲出Collection的例子:你有一些东西的集合,你实际上使用了ArrayList,因为为什么没有。如果你在将来使用LinkedList,你应该确保你的代码不会被破坏。
- "每个对象都有一个公开的接口"—嗯。不,不是吗?您列出的是这样的,但是很多都没有实现任何接口。
"编程到接口"发生在您使用库(您自己的代码中依赖的其他代码)时。然后,其他代码向您表示自身的方式、方法名、其参数、返回值等构成了您必须编程的接口。所以这是关于如何使用第三方代码的。
这也意味着,你不必关心你所依赖的代码的内部,只要接口保持不变,你的代码就是安全的(好吧,或多或少…)
从技术上讲,有更精细的细节,比如Java中所谓的"接口"的语言概念。
如果您想了解更多信息,可以问"实现接口"是什么意思…
它基本上意味着库中您将要使用的唯一部分应该依赖于它的API(应用程序编程接口),并且不应该将应用程序建立在库的具体实现之上。
假设你有一个图书馆给你一个stack。这个类提供了两种方法。比如push、pop、isempty和top。您应该只依赖这些来编写应用程序。违反这一点的一种方法是深入查看并发现堆栈是使用某种类型的数组实现的,这样,如果从空堆栈中弹出,您将得到某种索引异常,然后捕获它,而不是依赖类提供的isempty方法。如果库提供者从使用数组切换到使用某种列表,前者将失败,而后者将仍然工作,前提是提供者保持其API仍在工作。
我想这是埃里希·伽玛的咒语之一。我找不到他第一次描述它(在GoF的书之前),但是你可以在以下的访谈中看到它的讨论:http://www.artima.com/lejava/articles/designprinciples.html