接口或抽象类:使用哪一个?

316 php oop abstract-class interface

请解释我何时应该使用PHP interface以及何时使用PHP abstract class

我如何才能改变自己abstract class的态度interface

Ala*_*orm 448

如果要强制在系统中工作的开发人员(包括您自己)在他们将要构建的类上实现一定数量的方法,请使用接口.

如果要强制在系统中工作的开发人员(包括您自己)实现一定数量的方法,并且希望提供一些有助于他们开发子类的基本方法,请使用抽象类.

要记住的另一件事是客户端类只能扩展一个抽象类,而它们可以实现多个接口.因此,如果您在抽象类中定义行为合同,则意味着每个子类可能只符合单个合同.有时这是一件好事,当你想强迫你的用户程序员沿着特定的路径行进时.其他时候会很糟糕.想象一下,如果PHP的Countable和Iterator接口是抽象类而不是接口.

当您不确定要走哪条路时(如下面cletus所述),一种常见的方法是创建一个接口,然后让您的抽象类实现该接口.

  • 我整天都在努力理解`abstract`和`interface`类的用法,你的帖子一目了然.非常感谢Alan (12认同)
  • 抽象类的另一个优点是能够定义**抽象保护方法**.并不总是有用,但在某些架构中可以派上用场. (4认同)
  • @volocuga:不一定,就像艾伦指出的那样,只有一个摘要可以扩展.我个人不喜欢抽象实现接口的想法,因为它有助于代码混淆,而且不那么直接,IMO. (3认同)

kn3*_*n3l 160

an Abstract Class和an 之间的区别Interface:

抽象类

抽象类可以提供一些功能,剩下的就是派生类.

  • 派生类可以覆盖或不覆盖基类中定义的具体函数.

  • 从抽象类扩展的子类在逻辑上应该是相关的.

接口

接口不能包含任何功能.它包含的方法定义.

  • 派生类必须为接口中定义的所有方法提供代码.

  • 完全不同的和不相关的类可以使用接口在逻辑上组合在一起.

  • @Webinan在`抽象类X实现Y`中,你声明X的批量功能应该在派生类中实现,并且抽象类和派生类**必须**包含在Y中定义的函数,而`class X实现Y `只暗示类X必须包含Y中定义的函数.如果你的接口Y不是由任何其他类实现,那么XI实际上会跳过将Y定义为接口,并且只将Y中的函数实现为public/protected/private抽象函数,以确保它们在派生类中实现. (3认同)
  • “抽象类 X 实现 Y”和“类 X 实现 Y”有什么区别? (2认同)

Vin*_*kal 123

为什么要使用抽象类?以下是一个简单的例子.可以说我们有以下代码:

<?php 

class Fruit {
    private $color;

    public function eat() {
        // chew
    }

    public function setColor($c) {
        $this->color = $c;
    }
}

class Apple extends Fruit {
    public function eat() {
        // chew until core
    }
}

class Orange extends Fruit {
    public function eat() {
        // peeling
        // chew
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我给你一个苹果然后吃它.尝起来怎么样?它的味道像苹果.

<?php 
$apple = new Apple();
$apple->eat();

// Now I give you a fruit.
$fruit = new Fruit();
$fruit->eat();
Run Code Online (Sandbox Code Playgroud)

这味道是什么样的?嗯,它没有多大意义,所以你不应该这样做.这是通过使Fruit类抽象以及其中的eat方法来实现的.

<?php 
abstract class Fruit {
    private $color;

    abstract public function eat(){}

    public function setColor($c) {
        $this->color = $c;
    }
}
?>
Run Code Online (Sandbox Code Playgroud)

抽象类就像一个接口,但您可以在抽象类中定义方法,而在接口中它们都是抽象的.抽象类可以同时具有空和工作/具体方法.在接口中,那里定义的函数不能有一个体.在抽象类中,他们可以.

一个现实世界的例子:

<?php 
abstract class person {

    public $LastName;
    public $FirstName;
    public $BirthDate;

    abstract protected function write_info();
}

final class employee extends person{

    public $EmployeeNumber;
    public $DateHired;

    public function write_info(){
        //sql codes here
        echo "Writing ". $this->LastName . "'s info to emloyee dbase table <br>";   
    }
}

final class student extends person{

    public $StudentNumber;
    public $CourseName;

    public function write_info(){
        //sql codes here
        echo "Writing ". $this->LastName . "'s info to student dbase table <br>";
    }
}

///----------
$personA = new employee;
$personB = new student;

$personA->FirstName="Joe";
$personA->LastName="Sbody";

$personB->FirstName="Ben";
$personB->LastName="Dover";

$personA->write_info();
// Writing Sbody's info to emloyee dbase table
$personB->write_info();
// Writing Dover's info to student dbase table 
Run Code Online (Sandbox Code Playgroud)

  • +1`那味道怎么样?嗯,它没有多大意义,所以你不应该这样做.现在我知道抽象! (22认同)
  • 我爱你的水果榜样!自从我开始学习php之后,这个exmaples让我很清楚谢谢 (9认同)
  • 嗨,由于格式化的方式,这个答案可能得到了回报.如果它不是一个很大的代码块(四个空格会将某些东西放入代码块,将文本从块中取出来),如果这是从某个地方复制粘贴(它看起来像这样),那将会很好信任他们是礼貌的. (2认同)

cle*_*tus 65

最佳实践是使用接口来指定合同,并使用抽象类作为其一个实现.该抽象类可以填充很多样板文件,因此您可以通过覆盖您需要或想要的内容来创建实现,而不必强制您使用特定的实现.


Aus*_*gen 37

只是把它放到混合中,但正如Cletus提到的将接口与抽象类结合使用时,我经常使用界面来阐明我的设计思维.

例如:

<?php
class parser implements parserDecoratorPattern {
    //...
}
Run Code Online (Sandbox Code Playgroud)

这样,任何阅读我的代码的人(以及谁知道Decorator模式是什么)都会马上知道a)我如何构建我的解析器和b)能够看到用于实现装饰器模式的方法.

此外,我可能不在这里不是Java/C++/etc程序员,但数据类型可以在这里发挥作用.您的对象属于某种类型,当您以类型方式传递它们时会以编程方式进行操作.将可契约项移动到接口中只会指示方法返回的类型,而不是实现它的类的基类型.

现在已经很晚了,我想不出更好的psudo代码示例,但是这里有:

<?php
interface TelevisionControls {};
class Remote implements TelevisionControls {};
class Spouse implements TelevisionControls {};
Spouse spouse = new Spouse();
Remote remote = new Remote();
isSameType = (bool)(remote == spouse)
Run Code Online (Sandbox Code Playgroud)

  • 这是一个很棒的例子!但是,我认为你不能实例化一个抽象类,而是一个从抽象类扩展的类 (4认同)
  • 不会杀死使sudo代码至少看起来像有问题的语言.有人应该放一些$. (2认同)
  • 虽然这个例子很有趣,但我们无法直接实例化一个抽象类,请在示例中进行更改,以便人们可能不会认为它在PHP中是有效的用法. (2认同)

Mit*_*eat 13

主要区别在于抽象类可以包含默认实现,而接口则不能.

接口是行为契约,没有任何实现.


Thi*_*ous 13

另外,我想在此添加,因为任何其他OO语言都有某种接口和抽象,这并不意味着它们具有与PHP相同的含义和目的.抽象/接口的使用略有不同,而PHP中的接口实际上没有真正的功能.它们仅用于语义和与方案相关的原因.关键是要使项目尽可能灵活,可扩展且安全,以便将来扩展,无论开发人员以后是否有完全不同的使用计划.

如果您的英语不是本地的,您可能会查找抽象和接口实际上是什么.并寻找同义词.

这可能会帮助你作为一个比喻:

接口

比方说,你用草莓烤一种新蛋糕,然后你制作了描述成分和步骤的食谱.只有你知道为什么它的味道如此之好,你的客人喜欢它.然后你决定发布你的食谱,以便其他人也可以尝试那个蛋糕.

这里的要点是

- 做对
- 小心
- 防止可能变坏的东西(如草莓或其他东西)
- 为试用它的人保持容易
- 告诉你要做多久(如搅拌) )
- 告诉你可以做但不必做的事情

这正是描述接口的原因.它是一个指南,一组观察食谱内容的指令.就像你在PHP中创建一个项目一样,你想在GitHub或你的配偶或其他什么提供代码.界面是人们可以做的,不应该做的.持有它的规则 - 如果你不遵守它,整个结构将被打破.


ABSTRACTION

继续这个比喻在这里...想象一下,你这次是客人吃那块蛋糕.然后你现在正在尝试使用食谱的蛋糕.但是您想要添加新成分或更改/跳过配方中描述的步骤.接下来会发生什么?计划该蛋糕的不同版本.这次是用黑浆果而不是草莓浆和更多的香草奶油......美味.

这是你可以考虑的原始蛋糕的扩展.你基本上通过创建一个新的配方来对它进行抽象,因为它不同.它有一些新的步骤和其他成分.然而,黑莓版本有一些你从原版中接过的部分 - 这些是每种蛋糕必须具备的基本步骤.像牛奶一样的成分 - 这就是每个派生阶级所拥有的.

现在你想要交换成分和步骤,这些必须在那个蛋糕的新版本中定义.这些是必须为新蛋糕定义的抽象方法,因为蛋糕中应该有水果但是哪个?所以你这次采取黑浆果.完成.

你去了,你已经延长了蛋糕,按照界面和抽象的步骤和成分.


IEa*_*els 10

从哲学的角度来看:

  • 抽象类表示"是一种"关系.让我说我有水果,我会有一个水果抽象类,分享共同的责任和共同的行为.

  • 界面代表"应该做"的关系.在我看来,一个界面(这是一个初级开发者的意见),应该通过一个动作命名,或接近一个动作,(对不起,找不到这个词,我不是英语母语者)让我们说IEatable.你知道它可以吃,但你不知道你吃什么.

从编码的角度来看:

  • 如果您的对象具有重复的代码,则表明它们具有共同的行为,这意味着您可能需要一个抽象类来重用代码,这是您无法对接口执行的.

  • 另一个区别是对象可以根据需要实现尽可能多的接口,但由于"钻石问题",您只能拥有一个抽象类(请查看此处了解原因!http://en.wikipedia.org/wiki/ Multiple_inheritance#The_diamond_problem)

我可能会忘记一些观点,但我希望它可以澄清一些事情.

PS:Vivek Vermani的答案带来了"是一个"/"应该做",我不是故意要回答他的答案,只是为了重复使用这些术语,因为我喜欢它们!

  • 我相信,你正在寻找的这个词是可以食用的. (2认同)

bg1*_*7aw 10

添加一些已经很好的答案:

  • 抽象类让你提供一定程度的实现,接口是纯模板.接口只能定义功能,它永远不能实现它.

  • 任何实现接口的类都会提交实现它定义的所有方法,或者必须将其声明为abstract.

  • 接口可以帮助管理像Java一样不支持多重继承的事实.PHP类只能扩展单个父级.但是,您可以使类承诺实现任意数量的接口.

  • type:对于它实现的每个接口,该类采用相应的类型.因为任何类都可以实现接口(或更多接口),所以接口有效地连接不相关的类型.

  • 类可以扩展超类并实现任意数量的接口:

    class SubClass extends ParentClass implements Interface1, Interface2 {
        // ...
    }
    
    Run Code Online (Sandbox Code Playgroud)

请解释我何时应该使用接口以及何时应该使用抽象类?

当您只需提供一个没有实现的模板时,请使用接口,并且您希望确保实现该接口的任何类与实现它的任何其他类具有相同的方法(至少).

如果要为其他对象(部分构建的类)创建基础,请使用抽象类.扩展抽象类的类将使用定义/实现的一些属性或方法:

<?php
// interface
class X implements Y { } // this is saying that "X" agrees to speak language "Y" with your code.

// abstract class
class X extends Y { } // this is saying that "X" is going to complete the partial class "Y".
?>
Run Code Online (Sandbox Code Playgroud)

如何将抽象类更改为界面?

这是一个简化的案例/示例.取出任何实施细节.例如,从以下位置更改抽象类:

abstract class ClassToBuildUpon {
    public function doSomething() {
          echo 'Did something.';
    }
}
Run Code Online (Sandbox Code Playgroud)

至:

interface ClassToBuildUpon {
    public function doSomething();
}
Run Code Online (Sandbox Code Playgroud)


rag*_*147 6

抽象类和接口之间的技术差异已经精确地列在其他答案中.我想添加一个解释来在类和接口之间进行选择,同时为了面向对象的编程而编写代码.

类应表示实体,而接口应表示行为.

我们来举个例子吧.计算机监视器是一个实体,应该表示为一个类.

class Monitor{
    private int monitorNo;
}
Run Code Online (Sandbox Code Playgroud)

它旨在为您提供显示界面,因此功能应由界面定义.

interface Display{
    void display();
}
Run Code Online (Sandbox Code Playgroud)

还有很多其他的事情要考虑,正如其他答案中所解释的那样,但这是大多数人在编码时忽略的最基本的事情.

  • PHP没有定义返回类型,OP用`PHP`标记了这个问题 (2认同)