几年来,我是一名助教,负责编程模块的介绍 - 一年级本科生的Java。
大部分情况都很顺利,我们设法让学生们很好地接受了面向对象的编程,但学生很少看到的一点就是接口。
我们提供的任何解释几乎都是为了学习而设计的,或者与初学者的位置相差甚远。我们倾向于得到的反应是“我......看到”,翻译为“我不理解,听起来不太有用”。
这里的任何人都有成功教学生接口的方法吗?我不再是助教了,但总是唠叨我。
答案 0 :(得分:31)
如果你试图向初学者解释它,我会坚持认为接口可以在代码中促进代码重用和模块化:
例如,假设我们要绘制一些对象:
public class Painter {
private List<Paintable> paintableObjects;
public Painter(){
paintableObjects = new ArrayList<Paintable>();
}
public void paintAllObjects(){
for(Paintable paintable : paintableObjects){
paintable.paint();
}
}
}
public interface Paintable {
public void paint();
}
现在你可以向学生解释,如果没有Paintable接口,Painter对象需要有方法来绘制某些类型的对象,比如名为paintFences()
和paintRocks()
的方法,我们需要我们希望画家能够绘制的每种类型的对象都有一个新的Collection
。
但幸运的是,我们有接口使绘制对象变得轻而易举,对象的绘制方式完全取决于实现Paintable接口的类。
修改强>
我忘记提到的另一个好处是,如果你需要添加新对象来绘制代码库,你需要做的就是创建一个实现Paintable的新类,而Painter类永远不需要改变。从这个意义上讲,Painter类永远不会依赖于它要绘制的对象,它只需要能够绘制它们。
编辑2
James Raybould让我想起了我忘记提及的接口的关键用法:在组件之间建立一个接口,比如Paintable对象和Painter对象,可以让你更容易地与其他人一起开发。一个开发人员可以处理Painter对象,另一个可以处理Paintable对象,他们为了正常工作而必须做的就是事先定义一个他们都会使用的公共接口。我知道当我在大学水平项目中与其他人一起开展项目时,当你试图让每个人都在项目的不同部分工作并且最终所有组件都很好地结合在一起时,它真的很有帮助。
答案 1 :(得分:29)
在向非程序员解释界面和面向对象概念时,我总是使用家庭娱乐系统进行类比。
DVD播放器,电视,有线电视盒,遥控器都是封装复杂和复杂功能的对象。然而,它们彼此之间以及操作它们的人类之间的接口在很大程度上隐藏了这种复杂性中的大部分。
电视插孔中的视频是DVD播放机和有线电视盒以及其他一些设备实现的接口。
我怀疑学生可以用一种教育练习来完全使用Java代码描述他们自己的家庭娱乐系统。
答案 2 :(得分:16)
“哪里有什么东西,通常是接口做什么。所以我可能有车,但我永远不会去”开车“,但我可能会去驾驶......所以我的车可能会实现”可驾驶“界面。”
编辑:
马克提出了一个很好的观点。接口根本不做任何事情,而是定义发生什么行为。而且,他还提出了一个不想让观众感到困惑的好点。并不是说可以混淆经验丰富的开发人员,但混淆一个全新的学生绝对不是一个好主意。鉴于此,我正在修改我的单行班轮。“类定义存在,接口定义行为。类定义什么是什么,而接口定义什么东西。所以我可能有一辆车,它有像引擎,它有多少气体,它有什么历史MPG就是这样,但是我永远不会去“开车”。另一方面,我可能会去驾驶......我的车可以驾驶吗?如果我给它一个驾驶方法,它可以。我也可以“驾驶” “用驱动方法接口,然后把它留给汽车确定驾驶真正意味着什么。现在,如果我只有汽车那么拥有一个界面并不是什么大问题。但是卡车怎么样?如果它们都是可驾驶的,我对于他们两个人来说,他们可以简单地拥有一个List<Drivable
。当然,这位敏锐的学生说“为什么Car and Truck都不能简单地使用抽象的Drive方法扩展Vehicle?”这实际上是一个非常有效的概念。但是,航天飞机怎么样?汽车和卡车之间很少有部件适用于航天飞机,因此它似乎不适合延伸icle类。或者未来的汽车呢?我们不知道他们可能是什么样的,他们可能没有机会,他们可能只是能量泡沫让我们四处奔走,但我们仍然可以称他们的行为为drive()
。“
吐气
现在段落/论文有点冗长,但我可以看到,有些幻灯片或黑板,对于一年级学生来说是有效的(假设他们首先理解抽象课程)。
答案 3 :(得分:10)
我刚刚解释了与工作伙伴的接口,她正在从进度中学习java,并且她在开始时并没有得到所有OOP的东西所以我只是从非软件工程的角度解释了所有内容,我的解释是接口这样的东西:
假设您想聘请水管工来修理您房屋的某些事情,您不知道(并且您并不在意)您最终可能会雇佣谁,但您知道管道工必须能够做些什么。因此,您定义了一组任务,声称自己是管道工的人必须知道该怎么做。当然,每个人都可以有自己的方式执行每项任务,但最后,你招聘的人是水管工,因为他们知道如何完成每项任务。所以,如果你要在java中编写它,首先要做的是定义一个interface
管道工,如下所示:
public interface Plumber
{ //silly code here }
好吧,那么,假设我知道如何完成你要求的每项任务,所以我完全符合你的要求,所以根据你我是水管工。所以,今天我决定成为你的管道工,你决定雇用我(耶!),根据最后一个例子,你可以说我是一个知道如何以特定的方式开发软件和管道的人,如果我是为我写代码我可以写这样的东西:
public class Rick extends Person implements SoftwareDeveloper, Plumber
以后你可以用我作为你的管道工修理你家里的东西:
Plumber thePlumber = rick;
thePlumber.fixLeak(myHouse.bathroom.leak) // =(
从这一点开始,剩余的OOP概念很容易解释。
答案 4 :(得分:8)
好吧,最近,我碰巧向亲近的人解释了这一点。我解释“为什么是接口?”这个问题的方式是以USB端口和USB驱动器为例。
USB端口可视为规格,只要符合规范,任何USB驱动器都可以插入其中。所以在这种情况下,端口成为接口和众多类型的USB棒可用,成为一流。 前面提到这个例子,如果我要给某人提供一个USB驱动器(类),我不需要告诉他们(调用方法)我要经过什么。如果调用方法采用USB驱动器(类类型)作为参考,我将无法通过任何只有端口所针对的USB驱动器。
总结一下,Intefaces,帮助调用者与调用方法一致(在调用方法需要特定类型的实例的用例中),无论你传递的是什么实例,调用者也是如此因为被调用者确信它(实例)适合接口参考(用于类比的USB端口)。
答案 5 :(得分:7)
Class,我们花了最后几节实现quicksort。很难按姓名对人员名单进行排序。如果你不得不按年级排序这个清单,你现在会做什么?如果你不得不按年龄排序恐龙列表,你会怎么做?到目前为止,您唯一知道的方法是复制快速排序的代码,并更改比较及其操作的类型。这样可行 - 直到你发现那个总是困扰你的快速入口的难以捉摸的虫子,并且不得不将它分散在整个地方分散的几十个副本中。
今天,我们将学习更好的方法。
我们可以编写一个快速排序,而无需定义我们想要对列表进行排序的顺序,并在调用该快速排序时单独定义该顺序(并且只定义该顺序)。
[插入接口和多态的机制的解释,以比较器接口为例,这里]
现在,只有一个quicksort副本,而且只需要修复一次bug。此外,人们可以在不了解它的情况下使用quicksort(或者如果他们已经理解了它,而无需在想要对其进行排序时考虑其机制)。此外,编写快速排序的人不需要知道您需要对列表进行排序的顺序。因此,界面隔离了两组程序员,允许他们孤立地开发他们的软件部分。这就是为什么在许多编程语言中,你会在api中找到经过良好实现和测试的排序方法,即使这些方法的程序员不能知道所有类型的对象和人们想要在以后排序的命令。
答案 6 :(得分:6)
我通常使用“合同”,但“郑重承诺提供”也可能有助于理解。
答案 7 :(得分:5)
我建议Head First Design Pattern的第一章为此。 Duck模拟解释了使用继承的问题,本章的其余部分将介绍如何使用继承。
答案 8 :(得分:3)
这解释得最好:(引用此tutorial)
软件工程中存在许多情况,当不同的程序员群体同意一份说明其软件交互方式的“合同”时,这一点非常重要。每个组都应该能够编写代码,而不必知道如何编写其他组的代码。一般来说,接口就是这样的合同。 例如,想象一个未来主义社会,计算机控制的机器人汽车在没有人工操作员的情况下将乘客运送到城市街道。汽车制造商编写操作汽车的软件(当然是Java) - 停止,启动,加速,左转等等。另一个工业集团,电子制导仪器制造商,使计算机系统接收GPS(全球定位系统)位置数据和无线传输交通状况,并利用该信息来驾驶汽车。
汽车制造商必须发布行业标准界面,详细说明可以调用什么方法来使汽车行驶(任何汽车,来自任何制造商)。然后,指导制造商可以编写调用界面中描述的方法的软件来命令汽车。工业集团都不需要知道其他集团的软件是如何实施的。事实上,每个小组都认为其软件具有高度专有性,并保留随时修改的权利,只要它继续遵守已发布的界面。
更多链接:http://download-llnw.oracle.com/javase/tutorial/java/concepts/interface.html
答案 9 :(得分:2)
我输入这个作为对Harima555s答案的评论,但它扩大了。我想知道从另一端开始是否更有意义 - 在进入你的编写方式之前,让他们了解界面是如何有用的。
假设他们很好地掌握了继承,多态和抽象类。我可能首先回顾一下抽象课程,请一位学生解释一下。
接下来,介绍一个带有接口的类的示例,以克服角色/合同的概念。为简化起见,请从单个超类开始。
public class Rick extends Person implements SoftwareDeveloper, Plumber
public class Zoe extends Person implements SoftwareDeveloper, Chef
public class Paul extends Person implements Plumber, Chef
public class Lisa extends Person implements Plumber
不要解释太多,但要尝试让学生解决语法可能意味着什么 - 可能会显示一些引用Plumber或SoftwareDeveloper的代码。
问他们如何使用Person的继承来实现同样的目标。它们应该很快被卡住,或者提出多重继承。为避免以后讨论钻石问题,请说角色中没有重叠方法。
接下来,我试图克服相同界面可用于不同类型的类的想法。
public class Plane extends Vehicle implements Fly, PassengerTransport, Serviceable
public class Train extends Vehicle implements PassengerTransport, Serviceable
public class Bird extends Animal implements Fly
再次尝试让他们考虑如何使用公共超类和覆盖来实现相同的东西。
然后说明如何使用界面而不是类来编写多态代码 - 比如说出售PassengerTransport票证的TravelAgent。请详细说明 - 您可以编写适用于不同层次结构的类的多态代码。
此时,他们应该认为界面非常类似于能够将另一个超类添加到类中,并且已经掌握了多重继承的优点。
所以现在我们必须通过理解钻石问题来解释为什么这会使事情变得复杂,并且接口没有默认实现。
回到第一个例子,让他们了解如果SoftwareDeveloper和Plumber都有'MakeDrink'方法(一个制作Cola,另一个制作Coffee),我们在Rick上执行MakeDrink。
尝试并推动某人考虑如果MakeDrink在两个“超类”中保持抽象的想法消失了。在这一点上,从概念方面来看,我们应该准备好介绍定义接口的语法。
(我确实考虑过引入第二个原因 - 编写可应用于不同类层次结构的通用代码的难度,但发现你最终得到'为什么你不能从界面继承高度属性'或者太早讨论泛型编程。)
我认为现在我们应该通过mickey鼠标示例介绍这些概念 - 然后您可以回过头来解释正确的技术术语,并使用Java API中的实际示例。
答案 10 :(得分:2)
面向接口的设计比以往更好地描述了这一点http://pragprog.com/titles/kpiod/interface-oriented-design。作者使用了一些优秀的接口与继承的例子,比如动物王国的分类。它有一些反对过度继承和明智使用我迄今为止读过的接口的最佳论据。
一堆网站提供了不相容的方式:
Facebook.java 的列表:
public class Facebook {
public void showFacebook() {
// ...
}
}
YouTube.java 的列表:
public class YouTube {
public void showYouTube() {
// ...
}
}
StackOverflow.java 的列表:
public class StackOverflow {
public void showStackOverflow() {
// ...
}
}
客户端手动处理网站用来带来的不同方法 他们自己:
ClientWithoutInterface.java 的列表:
public class ClientWithoutInterface {
public static void main(String... args) {
String websiteRequested = args[0];
if ("facebook".equals(websiteRequested)) {
new Facebook().showFacebook();
} else if ("youtube".equals(websiteRequested)) {
new YouTube().showYouTube();
} else if ("stackoverflow".equals(websiteRequested)) {
new StackOverflow().showStackOverflow();
}
}
}
引入网站界面,让客户的工作更轻松:
Website.java 的列表:
public interface Website {
void showWebsite();
}
Facebook.java 的列表:
public class Facebook implements Website {
public void showWebsite() {
// ...
}
}
YouTube.java 的列表:
public class YouTube implements Website {
public void showWebsite() {
// ...
}
}
StackOverflow.java 的列表:
public class StackOverflow implements Website {
public void showWebsite() {
// ...
}
}
ClientWithInterface.java 的列表:
public class ClientWithInterface {
public static void main(String... args) {
String websiteRequested = args[0];
Website website;
if ("facebook".equals(websiteRequested)) {
website = new Facebook();
} else if ("youtube".equals(websiteRequested)) {
website = new YouTube();
} else if ("stackoverflow".equals(websiteRequested)) {
website = new StackOverflow();
}
website.showWebsite();
}
}
Whoop-de-doo,更多代码什么都没有?实际上我们可以更进一步 让客户绳索几个朋友帮助它找到并呈现一个 要求的网站:
ClientWithALittleHelpFromFriends.java 的列表:
public class ClientWithALittleHelpFromFriends {
public static void main(String... args) {
WebsiteFinder finder = new WebsiteFinder();
WebsiteRenderer renderer = new WebsiteRenderer();
renderer.render(finder.findWebsite(args[0]));
}
}
WebsiteFinder.java 的列表:
public class WebsiteFinder {
public Website findWebsite(String websiteRequested) {
if ("facebook".equals(websiteRequested)) {
return new Facebook();
} else if ("youtube".equals(websiteRequested)) {
return new YouTube();
} else if ("stackoverflow".equals(websiteRequested)) {
return new StackOverflow();
}
}
}
WebsiteRenderer.java 的列表:
public class WebsiteRenderer {
public void render(Website website) {
website.showWebsite();
}
}
回顾 ClientWithoutInterface ,它完全耦合到特定的查找和渲染。当你到达数百或数千个站点时,很难管理。通过网站界面,可以轻松地将 WebsiteFinder 转换为支持地图,数据库或基于网络的查找,以满足不断扩大的规模。
接口可以将角色与实现角色的组件分开。它们可以基于几乎任何东西将替代解决方案交换到同一个问题:
机器上的当前负载
数据集的大小(可以选择排序算法)
请求正在执行的操作的用户
答案 11 :(得分:2)
理解接口与理解多态性和IS-A关系并没有太大差别。由于通过实现接口或继承基类建立的关系,所有实现相同接口的类可以由程序统一操作为“基础”类型。
接口和基类之间的选择是一个设计决策。我会保持这么简单。
接口和继承的好处几乎相同。接口只是一个类型的抽象定义,而不是基类。
<强>更新强>
这是一个简单的程序,可用于演示类似的继承和接口。修改程序以使Base成为一个接口而不是一个类。在ClassA中,将“extends”替换为“implements”。该计划的结果将是相同的。
ClassB的目的是为了进一步说明类与其接口/基类之间关系的重要性。除非我们建立明确的关系,否则ClassB的实例可能不会传递给processBase,尽管它与Base有相似之处。
abstract class Base {
public void behavior() {};
};
class ClassA extends Base {
public void behavior() {
System.out.println("ClassA implementation of Base behavior");
}
};
class ClassB {
public void behavior() {
System.out.println("ClassB's version of behavior");
}
}
public class InterfaceExample {
public void processBase (Base i) {
i.behavior();
}
public static void main (String args[]) {
InterfaceExample example = new InterfaceExample();
example.processBase(new ClassA());
}
}
答案 12 :(得分:1)
你也教过JDBC吗?以它为例。这是一个很好的真实世界的例子,说明了强大的界面。在JDBC中,您正在针对几乎只存在接口的API编写代码。 JDBC驱动程序是具体实现。您可以轻松地在许多数据库上重用JDBC代码,而无需重写代码。您只需要切换JDBC驱动程序实现JAR文件和驱动程序类名称,以使其在另一个数据库上工作。
至少,使用接口可以在某种方式/点上改变具体实现(负责行为的代码逻辑),而无需重写整个代码。在解释事物时尝试使用真实世界的例子。这会更有意义。
答案 13 :(得分:1)
好吧,我最近发现了一种非常有用的界面使用方法。
我们有很多物品......
public class Settings { String[] keys; int values; }
public class Car { Engine engine; int value; }
public class Surface { int power; int elseInt; }
// and maaany more (dozens...)
现在,有人正在创建(即)表,并希望显示所有对象列表中的一些对象,但要在列表中显示对象,他必须编写返回String []的方法。
String[] toArrayString()
所以他只是在表
中需要的所有类中实现此方法public class Settings { String[] keys; int values; public String[] toArrayString {...} }
public class Car { Engine engine; int value; } // THIS NOT
public class Surface { int power; int elseInt; public String[] toArrayString {...} }
// and maaany more (dozens...)
现在,当他创建表格时,他正在写这样的smth
public void createTable() {
for(Object obj : allObjects) {
if(obj instanceof Settings) {
Settings settings = (Settings)obj;
table.add(settings.toArrayString());
}
if(obj instanceof Surface) {
// cast...
}
// etc multiple times...
}
}
使用界面,此代码可以更短,更易于阅读和维护:
public interface ISimpleInterface { String[] toArrayString; }
public class Settings implements ISimpleInterface { String[] keys; int values; public String[] toArrayString {...} }
public class Car { Engine engine; int value; } // THIS NOT
public class Surface implements ISimpleInterface { int power; int elseInt; public String[] toArrayString {...} }
public void createTable() {
for(Object obj : allObjects) {
if(obj instanceof ISimpleInterface) {
ISimpleInterface simple = (ISimpleInterface)obj;
table.add(simple.toArrayString());
}
}
}
此外,我们可以以非常干净和有效的方式实现多个接口而无需任何推导(推导有时是不可能的,不仅仅是在类已经使用某种其他推导的情况下)。
答案 14 :(得分:0)
在上一个问题中,有一些很好的场景可以解释使用接口背后的原因。
答案 15 :(得分:0)
很久以前,我读过一本书(不记得它的名字),它对接口有一个非常好的比喻。如果您(或您的学生)曾去过Cold Stone Creamery冰淇淋店,这听起来很熟悉。 Cold Stone有冰淇淋和冰淇淋,你可以在冰淇淋上添加几种不同的东西(在Cold Stone上称为混合物)。这些混合类似于接口。你的班级(或冰淇淋)可以有你想要的任意数量的接口(或混合)。添加界面(或混合)将添加该界面(或混合)的内容(或风味)到您的班级(或冰淇淋)。希望这有帮助!
答案 16 :(得分:0)
合同是关于接口的第一件事,但它们是用语言构建的,以提供多重继承的技能,并避免多重继承的复杂性。所以你可以教他们接口为程序添加运行时行为,或者你可以告诉学生可以使用接口来改变对象的运行时行为。
答案 17 :(得分:0)
首先,学生必须掌握抽象的概念。 当你(你= =学生)看到老师时,你可以形容他为老师...... 您也可以将他形容为(学校的)员工。 你可以形容他是一个人。 你将是正确的三次。那些是你可以给他的“头衔”。
他是一名教师,一名计算机科学教师,就像数学老师是一名教师一样。 它们处于相同的抽象层次。 现在,教师是一名雇员,就像看门人是雇员一样。 它们处于相同的抽象层次。 雇员是一个人,就像失业者是一个人一样。 它们处于相同的抽象层次。
(以UML的方式在板上画出整个东西)。
这就是将(大致)描述科学教师在社会中的地位的架构。
现在,抽象层次定义了一组共同的对象有什么共同点:所有教师都会教给学生并创建不可能的考试题目,以确保他们失败。所有学校的员工都为学校工作。
在编程中,接口是一个抽象层次。它描述了一组对象可以完成的操作。 每个对象都有一种独特的动作方式,但动作的类型是相同的。
以一些乐器为例:钢琴,吉他和长笛。 他们有什么共同点 ?音乐家可以演奏它们。 你不能要求音乐家吹奏3种乐器,但你可以让他演奏它们。
整个概念的架构如下:
界面(它们的共同点)是仪器。因为它们都是乐器:它们是一种共同的抽象概念。 他们可以共同做些什么?玩。因此,您定义了一个名为Play的抽象方法。
现在您无法定义“乐器”的播放方式,因为它取决于乐器的类型。 长笛是一种乐器。因此,Flute类实现了Instrument。 现在你必须定义音乐家在演奏那种乐器时会做些什么。 所以你定义了play方法。该定义将覆盖仪器的定义。 对另外两种乐器也一样。
现在,如果你有一个乐器列表,但不知道它们是什么类型,你仍然可以“要求”他们玩。 每个长笛都会吹。 每把吉他都会刮伤。 每个钢琴都会......呃... pianoted?无论如何!
但每个对象都知道如何执行“Play”操作。你不知道它们是什么样的乐器,但既然你知道它们是乐器,你就要求它们演奏并且他们知道如何做到这一点。
答案 18 :(得分:0)
接口提供了一个类需要做什么,例如你可以拥有一个Animal接口,并且假设有一个名为speak()的方法,每个动物都可以说,但是它们都是以不同的方式做的但是这允许你将任何实施动物的动物投射到动物身上,这样你就可以拥有一份动物名单并让它们全部说话,但要使用自己的实施方案接口只是这些东西的包装器。
答案 19 :(得分:0)
您可能还想比较Java和C ++中的接口(最终使用多重继承和/或“朋友”类)。
(至少,对我而言,这向我展示了Java中接口的简单/简单: - )
答案 20 :(得分:0)
我会告诉他们“接口定义提供了什么行为”和“实现提供这些行为”。使用界面的一段代码不需要 事情发生的细节,只需要知道会发生什么。
一个很好的例子是DAO模式。它定义了诸如“保存”,“加载”,“删除”之类的行为。您可以拥有一个适用于DB的实现,以及一个转到文件系统的实现。
我认为到目前为止,很多其他答案对于那些没有马上得到它的学生来说太复杂了......
答案 21 :(得分:0)
如果一个类需要处理一个抽象功能,并且不需要继承任何其他类,那么可以使用抽象类来公开功能,然后从中派生出真正的类。但请注意斜体字中的两个项目。接口使类可以表现为几种独立类型的抽象事物,即使该类是从另一个不像那些类型的事物那样的类派生的。因此,接口满足多重继承的主要用例之一,没有多重继承的ickiness。
一个非常实用的界面的简单真实示例:iEnumerable。如果一个类持有一些任意数量的某种类型的项,则对于另一个类对所有这些项进行操作非常有用,而不必担心持有它们的对象的细节。如果“enumerableThing”是一个抽象类,那么从不是“enumerableThing”的东西派生的任何类的对象都不可能传递给期望enumerableThing的代码。由于任何类(包括派生类)都可以实现enumerableThing而不考虑基类是否这样做,因此可以向任何类添加枚举能力。
答案 22 :(得分:0)
我认为,总的来说,实践学习总是有助于在讲座和例子之后强化概念。因此,与meriton建议的相同,我会提出相同程序的两个版本。 (quicksort就是一个很好的例子)
让学生多次修改每个程序,揭开程序中的微妙错误,让他们修复。我认为,您的学生很快就会发现,在设计程序时,接口会提供许多优势,而以后需要对其进行修改!
答案 23 :(得分:0)
我一直认为它是(口头)沟通的一种手段,因为(良好的沟通)是软件工程中最困难的事情。 Web服务和SOA也是如此。如果你给某人一个界面并说“请提供给我这项服务”。这是一种非常方便的方式,因为你不需要解释很多,编译器会检查他们是否做了正确的工作,而不是你! (我的意思是,不是真的,但至少它会确保方法存在)。
答案 24 :(得分:0)
除了其他答案,您可以尝试从不同的角度解释它。学生们我肯定已经知道继承了,因为它可能是讲课的第一个学生的每个学生的喉咙。他们听说过多重继承吗?方法解析被视为C ++(以及Perl和其他多继承语言)中的设计问题,因为从概念上来说,当在两个基类中定义的子类中调用方法时,应该发生什么样的模糊不清。都被执行了吗?哪一个先走?可以具体引用一个吗?另请参阅diamond problem。我的理解是,这种混乱只是通过引入没有实现的接口来解决,因此在方法解析过程中使用哪种实现没有任何歧义。
答案 25 :(得分:0)
接口的真正价值在于能够覆盖第三方API或框架中的组件。我将构建一个作业,学生需要覆盖预先构建的库中的功能,这些功能无法更改(并且没有源代码)。
更具体地说,假设您有一个“框架”,它生成一个实现为Page类的HTML页面。而page.render(stream)生成html。假设Page采用密封 ButtonTemplate类的实例。 ButtonTemplate对象有自己的render方法,因此在page.render(stream)中,只要有一个按钮,就会调用buttonTemplate.render(label,stream),它会为提交按钮生成html。作为学生的一个例子,假设我们想用链接替换那些提交按钮。
除了描述最终输出之外,我不会给他们太多方向。他们将不得不尝试各种解决方案。 “我们是否应该尝试解析按钮标签并替换为锚标签?我们可以将ButtonTemplate子类化为我们想要的吗?哦,等等。它是密封的!当他们密封这个类时他们在想什么!?!”< / em>然后在该赋值之后使用带有render(标签,流)方法的ILabeledTemplate接口显示第二个框架。