我一直听到大多数与节目相关的网站上的声明:
编程到接口而不是实现
但是我不明白其含义?
例子会有所帮助.
编辑:我收到了很多好的答案,所以你可以用一些代码片段来补充它,以便更好地理解这个主题.谢谢!
你可能正在寻找这样的东西:
public static void main(String... args) { // do this - declare the variable to be of type Set, which is an interface Set buddies = new HashSet(); // don't do this - you declare the variable to have a fixed type HashSet buddies2 = new HashSet(); }
为什么第一种方式做到这一点感觉很好?我们稍后会说你决定使用不同的数据结构,比如LinkedHashSet,以便利用LinkedHashSet的功能.代码必须像这样改变:
public static void main(String... args) { // do this - declare the variable to be of type Set, which is an interface Set buddies = new LinkedHashSet(); // <- change the constructor call // don't do this - you declare the variable to have a fixed type // this you have to change both the variable type and the constructor call // HashSet buddies2 = new HashSet(); // old version LinkedHashSet buddies2 = new LinkedHashSet(); }
这看起来不是那么糟糕吧?但是如果你用同样的方式写getter怎么办?
public HashSet getBuddies() { return buddies; }
这也必须改变!
public LinkedHashSet getBuddies() { return buddies; }
希望您看到,即使使用这样的小程序,您对声明变量类型的内容也会产生深远的影响.如果你只是依赖于一个被声明为接口的变量,而不是作为该接口的特定实现(在这种情况下,声明它是一个接口),那么对象来回这么多肯定有助于使程序更容易编码和维护设置,而不是LinkedHashSet或其他).它可以是这样的:
public Set getBuddies() { return buddies; }
还有另外一个好处,那个(至少对我而言)差异有助于我更好地设计一个程序.但希望我的例子给你一些想法...希望它有所帮助.
有一天,他的老板指示一名初级程序员编写一份应用程序来分析业务数据,并将其全部压缩在包含指标,图表和所有内容的漂亮报告中.老板给了他一个XML文件,上面写着"这是一些商业数据示例".
程序员开始编码.几个星期后,他觉得指标,图表和东西足以让老板满意,并且他展示了他的作品."这太棒了"老板说,"但是它还可以显示我们拥有的SQL数据库的商业数据吗?".
程序员回到编码.在他的应用程序中,有从XML中读取业务数据的代码.他重写了所有这些片段,用"if"条件包装它们:
if (dataType == "XML") { ... read a piece of XML data ... } else { .. query something from the SQL database ... }
当出现软件的新版本时,老板回答说:"这很好,但它是否也能报告来自这个Web服务的业务数据?" 记住所有那些乏味的陈述,他必须重写AGAIN,程序员变得愤怒."首先是xml,然后是SQL,现在是Web服务!什么是真正的业务数据来源?"
老板回答说:"任何可以提供它的东西"
那一刻,程序员开悟了.
我对该陈述的初步阅读与我读过的任何答案都非常不同.我同意所有使用接口类型为你的方法参数的人都非常重要,但这不是这个陈述对我意味着什么.
我的看法是,它告诉你编写的代码只取决于你正在使用的接口(在这种情况下,我使用"接口"来表示类或接口类型的公开方法),它在文档.这与编写代码相反,代码取决于您调用的函数的实现细节.您应该将所有函数调用视为黑盒子(如果两个函数都是同一类的方法,则可以对此进行例外处理,但理想情况下,它始终保持不变).
示例:假设有一个Screen
类Draw(image)
和Clear()
方法.文档说的是"draw方法在屏幕上绘制指定的图像"和"clear方法清除屏幕".如果要按顺序显示图像,正确的方法是重复调用,Clear()
然后按Draw()
.这将编码到界面.如果您正在编写实现代码,那么您可能会执行类似于调用Draw()
方法的操作,因为您在进行任何绘制之前通过查看Draw()
内部调用的实现来了解Clear()
.这很糟糕,因为您现在依赖于通过查看公开的界面无法了解的实现细节.
我期待看到是否有其他人在OP的问题中分享这句话的解释,或者如果我完全偏离基础......
这是一种在模块之间分离职责/依赖性的方法.通过定义特定的接口(API),可以确保接口任一侧的模块不会互相"打扰".
例如,假设模块1将负责显示特定用户的银行账户信息,而模块2将从"使用"后端获取银行账户信息.
通过定义一些类型和函数,以及相关参数,例如定义银行交易的结构,以及一些方法(函数),如GetLastTransactions(AccountNumber,NbTransactionsWanted,ArrayToReturnTheseRec)和GetBalance(AccountNumer),Module1将能够获取所需信息,而不用担心如何存储或计算此信息或其他任何信息.相反,Module2将通过按照定义的界面提供信息来响应方法调用,但不会担心显示,打印或其他任何信息的位置......
当模块改变时,接口的实现可能会有所不同,但只要接口保持不变,使用API的模块最坏的情况可能需要重新编译/重建,但它们不需要修改其逻辑.无论如何.
这就是API的想法.
接口定义了提交响应对象的方法.
当你的代码的接口,你可以改变的基础对象和你的代码仍然可以工作(因为你的代码是不可知的谁做执行任务或如何进行工作)你获得的灵活性这样.
当您对特定实现进行编码时,如果需要更改底层对象,则代码很可能会中断,因为新对象可能无法响应相同的方法.
所以举一个明显的例子:
如果您需要持有许多对象,则可能已决定使用Vector.
如果您需要访问Vector的第一个对象,您可以编写:
Vector items = new Vector(); // fill it Object first = items.firstElement();
到现在为止还挺好.
后来你决定,因为"某种"原因你需要改变实现(假设Vector因过度同步而产生瓶颈)
您意识到需要使用ArrayList instad.
好吧,你的代码会破...
ArrayList items = new ArrayList(); // fill it Object first = items.firstElement(); // compile time error.
你不能.这一行以及使用firstElement()方法的所有行都会中断.
如果你需要特定的行为,你肯定需要这个方法,它可能没问题(虽然你将无法改变实现) 但是如果你需要的是简单地检索第一个元素(也就是说,没有什么特别的其他具有firstElement()方法的Vector然后使用接口而不是实现将为您提供更改的灵活性.
List items = new Vector(); // fill it Object first = items.get( 0 ); //
在这种形式中,您不是编码为Vector的get方法,而是编写List的get方法.
底层对象如何执行该方法无关紧要,只要它响应"获取集合的第0个元素"的契约即可
这样您以后可以将其更改为任何其他实现:
List items = new ArrayList(); // Or LinkedList or any other who implements List // fill it Object first = items.get( 0 ); // Doesn't break
这个示例可能看起来很幼稚,但它是OO技术所依据的基础(即使是那些非静态类型的语言,如Python,Ruby,Smalltalk,Objective-C等)
一个更复杂的例子是JDBC的工作方式.您可以更改驱动程序,但大多数调用都将以相同的方式工作.例如,您可以使用oracle数据库的标准驱动程序,或者您可以使用Weblogic或Webpshere提供的更复杂的驱动程序.当然,你以前仍然需要测试你的产品并不神奇,但至少你没有像以下那样的东西:
statement.executeOracle9iSomething();
VS
statement.executeOracle11gSomething();
Java Swing也发生了类似的事情.
补充阅读:
设计模式的设计原则
有效Java项:通过其接口引用对象
(购买这本书是你在生活中可以做的最好的事情之一 - 当然,请阅读 - )