Interfaces允许您创建用于定义实现它的类的方法的代码 . 但是,您无法向这些方法添加任何代码 .
Abstract classes允许您执行相同的操作,同时向方法添加代码 .
现在如果你可以用抽象类实现相同的目标,为什么我们甚至需要接口的概念?
我被告知它与OO理论有关,从C到Java,这就是PHP的OO基础 . 这个概念在Java中有用但在PHP中没有用吗?它只是一种避免在抽象类中散落占位符的方法吗?我错过了什么吗?
Interfaces允许您创建用于定义实现它的类的方法的代码 . 但是,您无法向这些方法添加任何代码 .
Abstract classes允许您执行相同的操作,同时向方法添加代码 .
现在如果你可以用抽象类实现相同的目标,为什么我们甚至需要接口的概念?
我被告知它与OO理论有关,从C到Java,这就是PHP的OO基础 . 这个概念在Java中有用但在PHP中没有用吗?它只是一种避免在抽象类中散落占位符的方法吗?我错过了什么吗?
14 回答
接口的整个要点是让您可以灵活地让您的类实现多个接口,但仍然不允许多重继承 . 从多个类继承的问题是多种多样的,wikipedia页面总结得很好 .
接口是妥协 . 多重继承的大多数问题都不适用于抽象基类,因此现在大多数现代语言禁用多重继承,但是调用抽象基类接口并允许类根据需要“实现”多个 .
这个概念在面向对象编程中很有用 . 对我来说,我认为接口是 Contract . 很长一段时间,我的 class 和你的 class 都同意这个方法签名 Contract 我们可以“接口” . 至于抽象类,我认为更多的基类会删除一些方法,我需要填写详细信息 .
Why would you need an interface, if there are already abstract classes? 防止多重继承(可能导致多个已知问题) .
其中一个问题是:
资料来源:https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem
Why/When to use an interface? 一个例子......世界上所有的汽车都有相同的界面(方法)...
AccelerationPedalIsOnTheRight()
,BrakePedalISOnTheLeft()
. 想象一下,每个汽车品牌都会与其他品牌有所不同 . 宝马将在右侧制动,而本田将在车轮的左侧制动 . 每当他们购买不同品牌的汽车时,人们就必须了解这些是如何工作的 . 's why it'在多个"places."中拥有相同的接口是一个好主意界面为您做什么(为什么有人会使用一个)?界面可以防止您犯“错误”(它可以确保所有实现特定接口的类都具有接口中的方法) .
这样,
Create()
方法将始终以相同的方式使用 . 如果我们使用MySqlPerson
类或MongoPerson
类并不重要 . 我们使用方法的方式保持不变(界面保持不变) .例如,它将像这样使用(在我们的代码中的任何地方):
这样,这样的事情不可能发生:
记住一个界面并在任何地方使用相同的界面要比多个不同界面容易得多 .
这样,
Create()
方法的内部对于不同的类可以是不同的,而不会影响调用此方法的"outside"代码 . 所有外部代码必须知道的是方法Create()
有1个参数($personObject
),因为's how the outside code will use/call the method. The outside code doesn't关心方法内部发生了什么;它只需要知道如何使用/调用它 .你也可以在没有界面的情况下做到这一点,但如果使用界面,那就是"safer"(因为它可以防止你犯错) . 接口确保方法
Create()
在实现接口的所有类中具有相同的签名(相同类型和相同数量的参数) . 通过这种方式,您可以确保实现IPersonService
接口的任何类将具有方法Create()
(在此示例中)并且仅需要1个参数($personObject
)来调用/使用 .实现接口的类必须实现接口所具有的所有方法 .
我希望我不会重复太多 .
使用接口和抽象类之间的区别更多地与代码组织有关,而不是语言本身的强制执行 . 在为其他开发人员准备代码时,我经常使用它们,以便它们保持在预期的设计模式中 . 接口是一种“按 Contract 设计”,您的代码同意响应一组规定的API调用,这些API调用可能来自您没有使用的代码 .
虽然从抽象类继承是一种“是一种”关系,但这并不总是你想要的,而实现一个接口更像是一种“行为”关系 . 在某些情况下,这种差异可能非常显着 .
例如,假设您有一个抽象类帐户许多其他类扩展(帐户类型等) . 它有一组特定的方法,仅适用于该类型组 . 但是,其中一些帐户子类实现了Versionable,Listable或Editable,因此可以将它们抛出到期望使用这些API的控制器中 . 控制器不关心它是什么类型的对象
相比之下,我还可以创建一个不从Account扩展的对象,比如一个User抽象类,并且仍然实现Listable和Editable,但不是Versionable,这在这里没有意义 .
这样,我说FooUser子类不是一个帐户,但是DOES就像一个可编辑的对象 . 同样,BarAccount从Account扩展,但不是User子类,但实现了Editable,Listable和Versionable .
将可编辑,可列表和可版本化的所有这些API添加到抽象类本身不仅会混乱和丑陋,而且会复制Account和User中的公共接口,或强制我的User对象实现Versionable,可能只是为了抛出例外 .
接口本质上是您可以创建的蓝图 . 它们定义了类 must have 的方法,但是您可以在这些限制之外创建额外的方法 .
我不确定你不能为方法添加代码是什么意思 - 因为你可以 . 您是将接口应用于抽象类还是扩展它的类?
应用于抽象类的接口中的方法需要在该抽象类中实现 . 但是,将该接口应用于扩展类,该方法只需要在扩展类中实现 . 我可能在这里错了 - 我不尽可能经常使用接口 .
我一直认为接口是外部开发人员的模式或额外的规则集,以确保事情是正确的 .
您将在PHP中使用接口:
隐藏实现 - 为一类对象 Build 访问协议,更改底层实现,而不在您使用该对象的所有位置进行重构
检查类型 - 确保参数具有特定类型
$object instanceof MyInterface
在运行时强制执行参数检查
在单个类中实现多个行为(构建复杂类型)
这样一个
Car
对象现在start()
,stop()
(EngineInterface)或goRight()
,goLeft()
(转向界面)和其他我现在想不到的事情
4号它可能是最难以用抽象类解决的用例 .
来自Java思考:
我们看到抽象类和接口是相似的,因为它们提供了必须在子类中实现的抽象方法 . 但是,它们仍然存在以下差异:
希望这有助于任何人理解!
接口不是作为类可以扩展的基础而是作为所需函数的映射 .
以下是使用抽象类不适合的接口的示例:
假设我有一个日历应用程序,允许用户从外部源导入日历数据 . 我会编写类来处理导入每种类型的数据源(ical,rss,atom,json)这些类中的每一个都会实现一个公共接口,以确保它们都具有我的应用程序获取数据所需的公共公共方法 .
然后,当用户添加新的Feed时,我可以识别它的Feed类型,并使用为该类型开发的类来导入数据 . 为特定订阅源导入数据而编写的每个类都将具有完全不同的代码,除了需要实现允许我的应用程序使用它们的接口之外,类之间可能存在非常少的相似性 . 如果我要使用抽象类,我可以很容易地忽略这样一个事实,即我没有覆盖getEvents()方法,这会在这个实例中破坏我的应用程序,而使用接口不会让我的应用程序运行,如果有任何方法在接口中定义的实现它的类中不存在 . 我的应用程序不必关心它从Feed中获取数据所使用的类,只需要知道获取该数据所需的方法 .
为了更进一步,当我回到我的日历应用程序以添加另一个提要类型时,界面证明非常有用 . 使用ImportableFeed接口意味着我可以通过简单地添加实现此接口的新类来继续添加更多导入不同feed类型的类 . 这使我可以添加大量功能,而无需为我的核心应用程序添加不必要的批量,因为我的核心应用程序只依赖于接口所需的公共方法,只要我的新feed导入类实现了ImportableFeed界面然后我知道我可以把它放到位并继续移动 .
这只是一个非常简单的开始 . 然后,我可以创建另一个接口,可以实现我的所有日历类,以提供特定于类处理的feed类型的更多功能 . 另一个很好的例子是验证饲料类型等的方法 .
这超出了这个问题但是因为我使用了上面的例子:如果以这种方式使用,接口会出现自己的一组问题 . 我发现自己需要确保从实现的方法返回的输出以匹配接口并实现这一点我使用IDE读取PHPDoc块并将返回类型作为类型提示添加到接口的PHPDoc块中转换为实现它的具体类 . 我使用实现此接口的类的数据输出的类将至少知道它期望在此示例中返回一个数组:
没有太多空间来比较抽象类和接口 . 接口只是在实现时需要类具有一组公共接口的映射 .
在我看来,接口应该优于非功能抽象类 . 我不会感到惊讶,因为那里只有一个性能点击,因为只有一个对象实例化,而不是解析两个,组合它们(虽然,我不能确定,我不熟悉内部工作OOP PHP) .
确实,与Java相比,接口没有那么有用/有意义 . 另一方面,PHP6将引入更多类型提示,包括返回值的类型提示 . 这应该为PHP接口增加一些 Value .
tl; dr:interfaces定义了需要遵循的方法列表(思考API),而抽象类提供了一些基本/通用功能,子类可以根据特定需求进行优化 .
接口不仅仅是为了确保开发人员实现某些方法 . 这个想法是因为这些类保证有某些方法,即使你不知道类的实际类型,也可以使用这些方法 . 例:
在许多情况下,提供基类(抽象与否)是没有意义的,因为实现变化很大,并且除了一些方法之外不共享任何共同点 .
动态类型语言具有“鸭子打字”的概念,您不需要接口;您可以自由地假设该对象具有您正在调用它的方法 . 这解决了静态类型语言中的问题,其中对象有一些方法(在我的示例中,read()),但没有实现接口 .
在PHP中,你可以通过用逗号分隔它们来应用多个接口(我想,我没有找到一个干净的解决方案) .
至于多个抽象类,你可以有多个相互延伸的抽象(再次,我不完全确定,但我想我之前已经看到过) . 你唯一不能扩展的是最后一堂课 .
接口不会为您的代码提供任何性能提升或类似的东西,但它们可以在很大程度上使其可维护 . 确实可以使用抽象类(甚至非抽象类)来 Build 代码的接口,但是正确的接口(使用关键字定义的接口以及仅包含方法签名的接口)更容易整理并阅读 .
话虽这么说,我倾向于在决定是否在类上使用接口时使用自由裁量权 . 有时我想要默认方法实现,或者是所有子类共有的变量 .
当然,关于多接口实现的观点也是合理的 . 如果您有一个实现多个接口的类,则可以将该类的对象用作同一应用程序中的不同类型 .
然而,你的问题是关于PHP的事实使事情变得更有趣 . 在PHP中,键入接口仍然不是非常必要的,您可以在任何方法中提供任何内容,无论其类型如何 . 你可以静态地输入方法参数,但其中一些是破坏的(我相信String会导致一些打嗝) . 结合这一事实,您可以在尝试强制PHP中的静态类型方面具有很大的 Value (此时) . 正因为如此,PHP中的接口 Value 远远低于更强类型的语言 . 它们具有可读性的好处,但几乎没有 . 多重实现甚至不是有益的,因为您仍然需要声明方法并在实现者中赋予它们实体 .
以下是PHP接口的要点
它用于在类中定义所需的no方法[如果要加载html然后id和name是必需的,所以在这种情况下接口包括setID和setName] .
接口严格强制类包含其中定义的所有方法 .
您只能在具有公共可访问性的界面中定义方法 .
您还可以像类一样扩展接口 . 您可以使用extends关键字在php中扩展接口 .
扩展多个接口 .
如果两个共享函数具有相同名称,则无法实现2个接口 . 它会抛出错误 .
Example code :
接口就像你的基因 .
抽象类就像你真正的父母一样 .
它们的目的是遗传的,但在抽象类和接口的情况下,继承的是更具体的 .