请解释一下什么时候我应该使用PHP接口,什么时候我应该使用抽象类?

我如何能改变我的抽象类在一个接口?


当前回答

从哲学的角度来看:

An abstract class represents an "is a" relationship. Lets say I have fruits, well I would have a Fruit abstract class that shares common responsabilities and common behavior. An interface represents a "should do" relationship. An interface, in my opinion (which is the opinion of a junior dev), should be named by an action, or something close to an action, (Sorry, can't find the word, I'm not an english native speaker) lets say IEatable. You know it can be eaten, but you don't know what you eat.

从编码的角度来看:

如果您的对象有重复的代码,这表明它们有共同的行为,这意味着您可能需要一个抽象类来重用代码,而这是不能用接口实现的。 另一个区别是,一个对象可以实现任意多的接口,但是由于“菱形问题”,您只能有一个抽象类(查看这里了解原因!http://en.wikipedia.org/wiki/Multiple_inheritance The_diamond_problem)

我可能忘记了一些要点,但我希望它能澄清一些事情。

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

其他回答

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

类应该代表实体,而接口应该代表行为。

让我们举个例子。计算机显示器是一个实体,应该表示为一个类。

class Monitor{
    private int monitorNo;
}

它的设计目的是为您提供一个显示界面,因此功能应该由接口定义。

interface Display{
    void display();
}

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

为什么要使用抽象类?下面是一个简单的例子。假设我们有以下代码:

<?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
    }
}

现在我给你一个苹果,你吃了它。 它尝起来像什么?它尝起来像苹果。

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

// Now I give you a fruit.
$fruit = new Fruit();
$fruit->eat();

那是什么味道?这没什么意义,所以你不能这么做。这是通过使Fruit类抽象以及其中的eat方法来实现的。

<?php 
abstract class Fruit {
    private $color;

    abstract public function eat(){}

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

一个抽象类就像一个接口,但是你可以在一个抽象类中定义方法,而在一个接口中它们都是抽象的。抽象类可以有空方法和工作/具体方法。在接口中,定义的函数不能有主体。在抽象类中,它们可以。

一个真实的例子:

<?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 

只是想添加一个例子,说明什么时候你可能需要使用这两种方法。我目前正在编写一个通用ERP解决方案中绑定到数据库模型的文件处理程序。

我有多个抽象类,处理标准的crud,也有一些特殊的功能,如转换和流的不同类别的文件。 文件访问接口定义了一组用于获取、存储和删除文件的通用方法。

通过这种方式,我可以为不同的文件拥有多个模板,以及一组具有明显区别的公共接口方法。接口对访问方法进行了正确的类比,而不是对基抽象类的类比。

接下来,当我将为不同的文件存储服务制作适配器时,这种实现将允许在完全不同的上下文中在其他地方使用接口。

从哲学的角度来看:

An abstract class represents an "is a" relationship. Lets say I have fruits, well I would have a Fruit abstract class that shares common responsabilities and common behavior. An interface represents a "should do" relationship. An interface, in my opinion (which is the opinion of a junior dev), should be named by an action, or something close to an action, (Sorry, can't find the word, I'm not an english native speaker) lets say IEatable. You know it can be eaten, but you don't know what you eat.

从编码的角度来看:

如果您的对象有重复的代码,这表明它们有共同的行为,这意味着您可能需要一个抽象类来重用代码,而这是不能用接口实现的。 另一个区别是,一个对象可以实现任意多的接口,但是由于“菱形问题”,您只能有一个抽象类(查看这里了解原因!http://en.wikipedia.org/wiki/Multiple_inheritance The_diamond_problem)

我可能忘记了一些要点,但我希望它能澄清一些事情。

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

只是把这一点加入到混合中,但正如Cletus提到的将接口与抽象类结合使用一样,我经常使用接口来阐明我的设计思维。

例如:

<?php
class parser implements parserDecoratorPattern {
    //...
}

这样,任何阅读我的代码(并且知道Decorator Pattern是什么)的人都将立即知道a)我如何构建我的解析器和b)能够看到使用哪些方法来实现Decorator模式。

此外,我可能不是Java/ c++ /等程序员,但数据类型可以在这里发挥作用。你的对象是一个类型,当你传递它们时,类型在编程上很重要。将可收缩项移动到接口中只指定方法返回的类型,但不指定实现它的类的基类型。

现在已经很晚了,我想不出一个更好的伪代码示例,但如下所示:

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