Interfaces允许您创建定义实现它的类的方法的代码。但是,您无法向这些方法添加任何代码。
Abstract classes允许您执行相同的操作,同时向方法添加代码。
现在,如果您可以使用抽象类实现相同的目标,为什么我们甚至需要接口的概念?
我被告知它与OO理论有关,从C ++到Java,这就是PHP的OO基础。这个概念在Java中有用但在PHP中没有用吗?它只是一种避免在抽象类中乱丢占位符的方法吗?我错过了什么吗?
答案 0 :(得分:137)
接口的全部功能是让您可以灵活地让您的类实现多个接口,但仍然不允许多重继承。从多个类继承的问题很多而且各不相同,而且wikipedia页面对它们进行了很好的总结。
接口是妥协。多重继承的大多数问题都不适用于抽象基类,因此现在大多数现代语言禁用多重继承,但是调用抽象基类接口并允许类“实现”任意数量的那些。
答案 1 :(得分:121)
这个概念在面向对象编程中很有用。对我来说,我认为接口是合同。很长一段时间,我的班级和你的班级都同意这个方法签名合同我们可以“接口”。至于抽象类,我认为更多的基类会删除一些方法,我需要填写详细信息。
答案 2 :(得分:63)
如果已经有抽象类,为什么还需要一个接口? 防止多重继承(可能导致多个已知问题)。
其中一个问题:
“钻石问题”(有时被称为“钻石问题”) 死亡“)是两个B和C类继承时出现的歧义 来自A和D类继承自B和C.如果有方法 在A中,B和C已经覆盖,而D不会覆盖它,那么 D继承的方法的哪个版本:B的那个,或C的那个?
来源:https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem
为什么/何时使用界面?
一个例子......世界上所有的汽车都有相同的界面(方法)...... AccelerationPedalIsOnTheRight()
,BrakePedalISOnTheLeft()
。想象一下,每个汽车品牌都会拥有与其他品牌不同的“方法”。宝马将在右侧制动,而本田将在车轮的左侧制动。人们每次购买不同品牌的汽车时都必须了解这些“方法”是如何运作的。这就是为什么在多个“地方”拥有相同的界面是个好主意。
界面为您做什么(为什么有人会使用一个)? 一个接口可以防止你犯“错误”(它确保所有实现特定接口的类都具有接口中的方法)。
// Methods inside this interface must be implemented in all classes which implement this interface.
interface IPersonService
{
public function Create($personObject);
}
class MySqlPerson implements IPersonService
{
public function Create($personObject)
{
// Create a new person in MySql database.
}
}
class MongoPerson implements IPersonService
{
public function Create($personObject)
{
// Mongo database creates a new person differently then MySQL does. But the code outside of this method doesn't care how a person will be added to the database, all it has to know is that the method Create() has 1 parameter (the person object).
}
}
这样,Create()
方法将始终以相同的方式使用。如果我们使用MySqlPerson
类或MongoPerson
类,则无关紧要。我们使用方法的方式保持不变(界面保持不变)。
例如,它将像这样使用(我们的代码中的任何地方):
new MySqlPerson()->Create($personObject);
new MongoPerson()->Create($personObject);
这样,就不会发生这样的事情:
new MySqlPerson()->Create($personObject)
new MongoPerson()->Create($personsName, $personsAge);
更容易记住一个界面并在任何地方使用相同的界面,而不是多个不同的界面。
这样,Create()
方法的内部对于不同的类可以是不同的,而不会影响调用此方法的“外部”代码。所有外部代码必须知道方法Create()
有1个参数($personObject
),因为外部代码将使用/调用方法。外部代码不关心方法内部发生了什么;它只需要知道如何使用/调用它。
你也可以在没有界面的情况下做到这一点,但是如果使用界面,它会“更安全”(因为它可以防止你犯错误)。接口确保方法Create()
在实现接口的所有类中具有相同的签名(相同类型和相同数量的参数)。通过这种方式,您可以确保实现IPersonService
接口的任何类将具有方法Create()
(在此示例中)并且仅需要1个参数($personObject
)来调用/使用
实现接口的类必须实现接口所具有的所有方法。
我希望我不会重复太多。
答案 3 :(得分:23)
使用接口和抽象类之间的区别更多地与我的代码组织有关,而不是语言本身的强制执行。在为其他开发人员准备代码时,我经常使用它们,以便它们保持在预期的设计模式中。接口是一种“按合同设计”,您的代码同意响应一组规定的API调用,这些调用可能来自您没有使用的代码。
虽然从抽象类继承是一种“是一种”关系,但这并不总是你想要的,而实现一个接口更像是一种“行为”关系。在某些情况下,这种差异可能非常显着。
例如,假设您有一个抽象类帐户,许多其他类可以从该帐户扩展(帐户类型等)。它有一组特定的方法,仅适用于该类型组。但是,其中一些帐户子类实现了Versionable,Listable或Editable,因此可以将它们抛出到期望使用这些API的控制器中。控制器不关心它是什么类型的对象
相比之下,我还可以创建一个不从Account扩展的对象,比如一个User抽象类,并且仍然实现Listable和Editable,但不是Versionable,这在这里没有意义。
通过这种方式,我说FooUser子类不是一个帐户,但是DOES就像一个可编辑的对象。同样,BarAccount从Account扩展,但不是User子类,但实现了Editable,Listable和Versionable。
将可编辑,可列表和可版本化的所有这些API添加到抽象类本身不仅会混乱和丑陋,而且会复制Account和User中的公共接口,或强制我的User对象实现Versionable,可能只是抛出异常。
答案 4 :(得分:20)
接口本质上是您可以创建的蓝图。它们定义了类必须具有的方法,但您可以在这些限制之外创建额外的方法。
我不确定你无法向方法添加代码是什么意思 - 因为你可以。您是将接口应用于抽象类还是扩展它的类?
应用于抽象类的接口中的方法需要在该抽象类中实现。但是,将该接口应用于扩展类,该方法只需要在扩展类中实现。我可能在这里错了 - 我不经常使用接口。
我一直认为接口是外部开发人员的模式,或者是额外的规则集,以确保事情正确。
答案 5 :(得分:11)
您将在PHP中使用接口:
$object instanceof MyInterface
类Car实现EngineInterface,BodyInterface,SteeringInterface {
Car
对象现在可以start()
,stop()
(EngineInterface)或goRight()
,goLeft()
(转向界面)
以及我现在想不到的其他事情
它可能是最明显的用例,你无法用抽象类来解决这个问题。
来自Java思考:
一个接口说,“这就是实现这个特定接口的所有类都会是这样的。”因此,任何使用特定接口的代码都知道可以为该接口调用哪些方法,就是这样。因此,该接口用于在类之间建立“协议”。
答案 6 :(得分:9)
接口不是作为类可以扩展的基础,而是作为所需函数的映射。
以下是使用抽象类不适合的接口的示例:
假设我有一个日历应用程序,允许用户从外部源导入日历数据。我会编写类来处理导入每种类型的数据源(ical,rss,atom,json)这些类中的每一个都会实现一个公共接口,以确保它们都具有我的应用程序获取数据所需的公共公共方法。
<?php
interface ImportableFeed
{
public function getEvents();
}
然后,当用户添加新的Feed时,我可以识别它的Feed类型,并使用为该类型开发的类来导入数据。为特定订阅源导入数据而编写的每个类都将具有完全不同的代码,除了需要实现允许我的应用程序使用它们的接口之外,类之间可能存在非常少的相似性。如果我要使用抽象类,我可以很容易地忽略这样一个事实,即我没有覆盖getEvents()方法,这会在这个实例中破坏我的应用程序,而使用接口不会让我的应用程序运行,如果有任何方法在接口中定义的实现它的类中不存在。我的应用程序不必关心它从Feed中获取数据所使用的类,只需要知道获取该数据所需的方法。
为了更进一步,当我回到我的日历应用程序以添加另一个提要类型时,界面证明非常有用。使用ImportableFeed接口意味着我可以通过简单地添加实现此接口的新类来继续添加更多导入不同Feed类型的类。这允许我添加大量功能,而不必为我的核心应用程序添加不必要的批量,因为我的核心应用程序只依赖于接口需要的公共方法,只要我的新feed导入类实现ImportableFeed接口然后我知道我可以放下它并继续前进。
这只是一个非常简单的开始。然后,我可以创建另一个接口,可以实现我的所有日历类,以提供特定于类处理的feed类型的更多功能。另一个很好的例子是验证饲料类型等的方法
这超出了问题但是因为我使用了上面的例子: 如果以这种方式使用,接口会出现各自的问题。我发现自己需要确保从实现的方法返回的输出以匹配接口并实现这一点我使用读取PHPDoc块的IDE并将返回类型作为类型提示添加到接口的PHPDoc块中转换为实现它的具体类。我的类使用实现此接口的类的数据输出,至少知道它期望在这个例子中返回一个数组:
<?php
interface ImportableFeed
{
/**
* @return array
*/
public function getEvents();
}
没有足够的空间来比较抽象类和接口。接口只是在实现时需要类具有一组公共接口的映射。
答案 7 :(得分:7)
接口不仅仅是为了确保开发人员实现某些方法。这个想法是因为这些类保证有某些方法,即使你不知道类的实际类型,也可以使用这些方法。例如:
interface Readable {
String read();
}
List<Readable> readables; // dunno what these actually are, but we know they have read();
for(Readable reader : readables)
System.out.println(reader.read());
在许多情况下,提供基类(抽象与否)是没有意义的,因为实现的变化很大,并且除了一些方法之外不会共享任何共同点。
动态类型语言有“鸭子打字”的概念,你不需要接口;您可以自由地假设该对象具有您正在调用它的方法。这解决了静态类型语言中的问题,其中对象有一些方法(在我的例子中,read()),但没有实现接口。
答案 8 :(得分:5)
在我看来,接口应该优于非功能抽象类。我不会感到惊讶,因为那里只有一个性能上升,因为只有一个对象实例化,而不是解析两个,组合它们(虽然,我不能确定,我不熟悉内部工作OOP PHP)。
与Java相比,接口确实没那么有用/有意义。另一方面,PHP6将引入更多类型提示,包括返回值的类型提示。这应该为PHP接口增加一些价值。
tl; dr:interfaces定义了需要遵循的方法列表(思考API),而抽象类提供了一些基本/通用功能,子类可以根据特定需求进行优化。
答案 9 :(得分:4)
我不记得PHP在这方面是否有所不同,但在Java中,您可以实现多个接口,但不能继承多个抽象类。我假设PHP的工作方式相同。
在PHP中,你可以通过用逗号分隔它们来应用多个接口(我想,我没有找到一个干净的解决方案)。
对于多个抽象类,你可以有多个相互延伸的摘要(同样,我不完全确定,但我想我之前已经看过了)。你唯一不能扩展的是最后一堂课。
答案 10 :(得分:3)
接口不会给你的代码带来任何性能提升或类似的东西,但它们可以在很大程度上使其可维护。确实可以使用抽象类(甚至非抽象类)来建立代码的接口,但是正确的接口(使用关键字定义的接口以及仅包含方法签名的接口)更容易排序并阅读。
话虽如此,在决定是否在课堂上使用界面时,我倾向于谨慎使用。有时我想要默认方法实现,或者所有子类都通用的变量。
当然,关于多接口实现的观点也是合理的。如果您有一个实现多个接口的类,则可以将该类的对象用作同一应用程序中的不同类型。
然而,你的问题是关于PHP的事实使事情变得更有趣。在PHP中,键入接口仍然不是非常必要的,您可以在任何方法中提供任何内容,无论其类型如何。你可以静态地输入方法参数,但其中一些是破坏的(我相信String会导致一些打嗝)。再加上你不能输入大多数其他引用的事实,并且尝试在PHP中强制静态类型没有多大价值(此时)。正因为如此,PHP 中的接口,此时的值远小于更强类型的语言。它们具有可读性的好处,但几乎没有。多重实现甚至不是有益的,因为你仍然需要声明方法并在实现者中赋予它们主体。
答案 11 :(得分:2)
界面就像您的基因。
抽象类就像您的实际父母。
它们的用途是遗传的,但是对于抽象类与接口,继承的内容更为具体。
答案 12 :(得分:1)
以下是PHP接口的要点
示例代码:
interface test{
public function A($i);
public function B($j = 20);
}
class xyz implements test{
public function A($a){
echo "CLASS A Value is ".$a;
}
public function B($b){
echo "CLASS B Value is ".$b;
}
}
$x = new xyz();
echo $x->A(11);
echo "<br/>";
echo $x->B(10);
答案 13 :(得分:1)
我们看到抽象类和接口是相似的,因为它们提供了必须在子类中实现的抽象方法。但是,它们仍然存在以下差异:
1.Interfaces可以包含抽象方法和常量,但不能包含具体的方法和变量。
2.界面中的所有方法都必须位于公开可见性中 范围。
3.A类可以实现多个接口,但它可以继承 只来自一个抽象类。
interface abstract class
the code - abstract methods - abstract methods
- constants - constants
- concrete methods
- concrete variables
access modifiers
- public - public
- protected
- private
etc.
number of parents The same class can implement
more than 1 interface The child class can
inherit only from 1 abstract class
希望这有助于任何人理解!
答案 14 :(得分:1)
我不了解其他语言,那里的接口是什么概念。但是对于PHP,我将尽力解释。请耐心等待,如果有帮助,请发表评论。
接口充当“合同”,指定一组子类的功能,但不指定其操作方式。
规则
接口无法实例化。
您不能在接口中实现任何方法,即它仅包含方法的.signature,而不包含details(body)。
接口可以包含方法和/或常量,但不能包含属性。接口常量与类常量具有相同的限制。接口方法是隐式抽象的。
- 接口不得声明构造函数或析构函数,因为这些是类的实现详细信息 级别。
- 界面中的所有方法必须具有公共可见性。
现在让我们举一个例子。 假设我们有两个玩具:一个是狗,另一个是猫。
我们知道狗叫和猫叫,这两种说话方法相同,但功能或实现不同。 假设我们为用户提供了一个带有语音按钮的遥控器。
当用户按下“说话”按钮时,玩具必须说话,无论是狗还是猫都没关系。
这是使用接口而不是抽象类的一个好案例,因为实现是不同的。 为什么?记住
如果需要通过添加一些非抽象方法来支持子类,则应使用抽象类。否则,接口将是您的选择。