2009-06-27 46 views
3

因为我了解它们是契约的接口,我将它解释为契约词,即必须具有接口中指定的内容(例如打开,关闭,读取,写入接口处理文件)。接口的功能主要是在不知道类是如何构建的情况下使用函数的?

但是,我很难掌握的是,为什么你需要一个接口来告诉你类必须能够做什么,你是否已经知道它已经写在接口规范中了?

我可以看到接口的唯一原因是在大型项目中,您希望能够使用类而不真正知道它是如何构建的。通过查看界面需要什么可以让你知道如何使用它。

这使我想知道为什么我应该使用(或者如果我应该)接口的项目,我将是唯一一个工作。林相当肯定有更多的用途,我没看到。

我把我的大部分假设和解释从this questionthis vbforums post

回答

1

假设您正在编写一组实现枪支的类。你可能有一把手枪,一把步枪和一把机枪。然后,假设你决定以这样一种方式使用这些类,以便对这些枪的每一个执行fire()动作。你可以这样做:

private Pistol p01; 
private Pistol p02; 
private Rifle r01; 
private MachineGun mg01; 

public void fireAll() { 
    p01.fire(); 
    p02.fire(); 
    r01.fire(); 
    mg01.fire(); 
} 

这种烂,因为如果你添加或删除枪,你必须改变一些地方的代码。或者更糟糕的是,假设您希望能够在运行时添加和删除枪支:它变得更加困难。

让我们制作一个上面的每个枪都会实现的接口,称之为枪支。现在我们可以做到这一点。

private Firearm[] firearms; 

public void fireAll() { 
    for (int i = 0; i < firearms.length; ++i) { 
     firearms[i].fire(); 
    } 
} 

这有助于改变一点点,不是吗?

+0

很好的例子,但是对于一个叫做Firearm的抽象基类也是如此(这会在上下文中更有意义)。 – 2009-06-27 02:56:51

+0

当然,接口可以在抽象基类中定义。这就是你用C++定义接口的方式,它没有单独的接口语法。 – dewtell 2009-06-27 03:06:17

4

你说得对在该界面指定合约,但implementaiton可能有天壤之别。

简单的例子:在Java中列出。列表是一个接口。两个常见的实现是ArrayList和LinkedList。每个人都表现不同,但是尊重同一份合同。我的意思是说,ArrayList具有O(1)(常量)访问,而LinkedList具有O(n)访问。

如果你还不明白O(1)和O(n)是什么意思,我建议你看看Plain english explanation of Big O

你这样做,甚至你自己的代码(即东西是不能或不会是一个公共API)的原因是:

  • 便于单元测试:你可以嘲笑了,而接口你不能(或不能轻易地)模拟一堂课;和
  • 允许您稍后更改实现而不影响调用代码。
2

但是有一个很难把握。这就是为什么你需要有一个告诉你是什么类必须能够在所有做一个接口,难道不知道IM已经因为你在写接口规范?

当你编写外部可用的代码时,这也很好。在这种情况下,代码编写器不是Interface的用户。如果要向用户提供库,则可能只需记录Interface,并允许Class根据上下文更改或随时间变化而不更改Interface

+0

一个很好的例子就是Windows API,它具有版本化接口,因为随着API的发展,这些年来,即, IMyInterface,IMyInterface2,IMyInterface3。假设客户端代码将使用它需要的最低公分母来获得它所需要的功能,这将会使您的代码向后兼容到实现所需接口的任何版本。 – 2009-06-27 02:55:01

0

如果您想重温您的旧代码,您会感谢您为自己构建了一些界面。没有什么比想要实现一种存在的新类型更令人沮丧的,只是意识到你不记得一个新对象必须拥有什么。在Java中,您可以实现多种接口,这种接口可以模拟多重继承(具有多个父对象的对象)。你只能扩展一个超类。

3

当你有两个类需要一起工作但应尽可能彼此分离时,接口很有用。一个常见的例子是当您使用listenersmodel-view-controller设计模式中将模型和视图连接在一起时。

例如,假设您有一个GUI应用程序,用户可以在其中登录和注销。当用户注销时,你可能会说,改变你的“当前登录为即时”标签并关闭所有可见的对话窗口。

现在你有一个User类和logOut方法,并且每当logOut被调用时,你都希望发生所有这些事情。要做到这一点的方法之一是有logOut方法处理所有这些任务:

// Bad! 
public void logOut() { 
    userNameLabel.setText("Nobody is logged in"); 
    userProfileWindow.close(); 
} 

这是因为在你的User类现在紧耦合到您的GUI皱起了眉头。如果User课程变得笨拙并且做得不那么好,那会更好。而不是关闭userProfileWindow本身,它应该告诉userProfileWindow用户已注销并让userProfileWindow做它想做的任何事情(它想要关闭它自己)。

要做到这一点的方法是创建一个通用的UserListener接口和方法loggedOut,该方法在用户注销时由User类调​​用。任何想知道用户何时登录和注销的人都会在处实现这个接口。

public class User { 
    // We'll keep a list of people who want to be notified about logouts. We don't know 
    // who they are, and we don't care. Anybody who wants to be notified will be 
    // notified. 
    private static List<UserListener> listeners; 

    public void addListener(UserListener listener) { 
     listeners.add(listener); 
    } 

    // This will get called by... actually, the User class doesn't know who's calling 
    // this or why. It might be a MainMenu object because the user selected the Log Out 
    // option, or an InactivityTimer object that hasn't seen the mouse move in 15 
    // minutes, who knows? 
    public void logOut() { 
     // Do whatever internal bookkeeping needs to be done. 
     currentUser = null; 

     // Now that the user is logged out, let everyone know! 
     for (UserListener listener: listeners) { 
      listener.loggedOut(this); 
     } 
    } 
} 

// Anybody who cares about logouts will implement this interface and call 
// User.addListener. 
public interface UserListener { 
    // This is an abstract method. Each different type of listener will implement this 
    // method and do whatever it is they need to do when the user logs out. 
    void loggedOut(User user); 
} 

// Imagine this is a window that shows the user's name, password, e-mail address, etc. 
// When the user logs out this window needs to take action, namely by closing itself so 
// this information isn't viewable by other users. To get notified it implements the 
// UserListener interface and registers itself with the User class. Now the User.logOut 
// method will cause this window to close, even though the User.java source file has no 
// mention whatsoever of UserProfileWindow. 
public class UserProfileWindow implements UserListener { 
    public UserProfileWindow() { 
     // This is a good place to register ourselves as interested observers of logout 
     // events. 
     User.addListener(this); 
    } 

    // Here we provide our own implementation of the abstract loggedOut method. 
    public void loggedOut(User user) { 
     this.close(); 
    } 
} 

操作的顺序如下所示:

  1. 应用程序启动并在用户登录她打开UserProfileWindow
  2. UserProfileWindow将自身添加为UserListener
  3. 用户闲置并且不会触摸键盘或鼠标15分钟。
  4. 想象中的InactivityTimer班级公告和呼吁User.logOut
  5. User.logOut更新模型,清除currentUser变量。现在如果有人问,没有人登录。
  6. User.logOut循环遍历其收听者列表,每个收听者呼叫loggedOut()
  7. UserProfileWindowloggedOut()方法被调用,它关闭窗口。

这很好,因为这个User类完全不知道谁需要了解注销事件。它不知道用户名称标签需要更新,配置文件窗口需要关闭,没有。如果稍后我们决定在用户​​注销时需要做更多事情,那么User类完全不需要改变。

所以,监听模式是接口超级有用的一个例子。接口都是关于解耦的类,去除了需要彼此交互的类之间的关系和依赖关系,但是不应该在它们的代码之间有很强的关系。

+0

这对我来说是惊人的东西,在更多方面是一个真正的eyeopener,然后只是界面使用。 但是为了更多地打开它们,我希望看到代码片段的其余部分与“实际操作”中的内容相关联。你能不能再粘贴一些? :) – 2009-06-27 03:17:19

+0

非常感谢你的解释编辑!现在我头上的灯泡尺寸是巨大的。以这种方式使用接口是特定的(还是更常见)Java? – 2009-06-27 03:38:37

1

比方说,你有两个类汽车和大猩猩。这两个类没有任何关系。但是,让我们假设你也有一个可以粉碎事物的课程。而不是定义,需要一个车,并粉碎,然后有一个单独的方法,它是猩猩,它粉碎的方法,可以使称为ICrushable接口...

interface ICrushable 
{ 
    void MakeCrushingSound(); 
} 

现在你可以有你的车,你的大猩猩实施ICrushable和您的爱车实现然后ICrushable和你的破碎机上ICrushable,而不是一辆车,一个大猩猩操作...

public class Crusher 
{ 
    public void Crush(ICrushable target) 
    { 
    target.MakeCrushingSound(); 
    } 
} 

public class Car : ICrushable 
{ 
    public void MakeCrushingSound() 
    { 
     Console.WriteLine("Crunch!"); 
    } 
} 

public class Gorilla : ICrushable 
{ 
    public void MakeCrushingSound() 
    { 
     Console.WriteLine("Squish!!"); 
    } 
} 

static void Main(string[] args) 
{ 
    ICrushable c = new Car();  // get the ICrushable-ness of a Car 
    ICrushable g = new Gorilla(); // get the ICrushable-ness of a Gorilla 

    Crusher.Crush(c); 
    Crusher.Crush(g); 
} 

和中提琴!你有一台粉碎机可以粉碎汽车并获得“紧缩”!并可以粉碎大猩猩并获得“Squish!”。无需经过查找Cars和Gorillas之间的类型关系以及编译时类型检查(而不是运行时切换语句)的过程。

现在,考虑一些不那么愚蠢的事情......一个可以比较的类(例如IComparable)。这个类将定义你如何比较它的两个类型。

每个评论:好吧,让我们来做吧,这样我们可以对大猩猩数组进行排序。首先,我们添加一些东西来进行排序,比如重量(请忽略按重量排序大猩猩的可疑业务逻辑......在这里不重要)。然后,我们实现ICompararble ...

public class Gorilla : ICrushable, IComparable 
{ 
    public int Weight 
    { 
     get; 
     set; 
    } 

    public void MakeCrushingSound() 
    { 
     Console.WriteLine("Squish!!"); 
    } 

    public int CompareTo(object obj) 
    { 
     if (!(obj is Gorilla)) 
     { 
      throw (new ArgumentException()); 
     } 

     var lhs = this; 
     var rhs = obj as Gorilla; 

     return (lhs.Weight.CompareTo(rhs.Weight)); 
    } 

} 

通知我们“得到了各地”的single inheritance的限制,许多语言都有。我们被允许实现尽可能多的接口。现在,通过这样做,我们可以使用10多年前写在我刚刚写的类上的功能(Array.Sort,Array.BinarySearch)。现在,我们可以编写如下代码...

var gorillas = new Gorilla[] { new Gorilla() { Weight = 900 }, 
           new Gorilla() { Weight = 800 }, 
           new Gorilla() { Weight = 850 } 
}; 

Array.Sort(gorillas); 
var res = Array.BinarySearch(gorillas, 
    new Gorilla() { Weight = 850 }); 

我的大猩猩得到分类和二进制搜索查找匹配的大猩猩与850

0

没有人强迫你写接口的体重并没有语言甚至强制执行。它是一个好程序员会遵循的最佳实践和成语。你是唯一使用你的代码的人,你可以写出你喜欢的东西,但是如果你离开这个项目而其他人需要维护和/或扩展它,该怎么办?或者如果其他项目考虑使用你的代码呢?或者甚至如果过了一段时间,您必须重新访问您的代码以添加功能或重构?你会为这些事情创造一场噩梦。很难理解你的客体关系和合同建立在他们之间。

0

抽象: 使用接口编写的代码是可重用的,永远不需要改变。在下面的情况下,子将与System.Array,System.ArrayList,System.Collection.CollectionBase,T的列表一起工作,因为它们都实现了IList。即使当类继承另一个类时,现有的类也可以轻松实现接口。 你甚至可以写你的班级来实施IList给我们。或者另一个程序也可以实现在子接口中使用的接口。

公共子DoSomething的(BYVAL值作为IList的) 端子

您还可以使用多个接口的类,这样一类既可以是的IList和IEnumerable,在大多数语言中,您可以在继承一个类。

我也会看看它们是如何在各种框架中使用的。

0

正如我理解你的问题,为什么我们需要接口?对 ? 嗯,我们不需要他们:)

在C++中,例如,当你定义一个模板......再说,看起来虚函数像::

template <typename T> 
void fun(const T& anObjectOfAnyType) 
{ 
    anyThing.anyFunction(); 
} 

,你可以在任何地方使用该功能任何具有函数称为anyFunction的函数... 编译器要做的唯一一件事就是用类型名称 替换T并编译新的代码片段...

This事实上很容易出错。原因是,如果我们插入一个没有任何函数的类型,那么我们会得到一个错误,每次错误都不一样,编译器无法翻译的每一行都会为它发出一个错误。你只会失去很多错误! 新类型没有所需的功能,例如我们的乐趣正常工作。

现在接口解决了这个问题,怎么样? 如果类型具有所需的功能,那么它是合适的,如果不是那么编译器会发出一个类型不适合的错误。

模板示例仅供说明,如果您想要对如果java没有接口会发生什么情况进行成像,那么您唯一需要做的就是在每个类中手动检查每个函数的存在,你认为这个类实现了一个特定的功能。肮脏的工作是由编译器:)

由于完成,

相关问题