一、Abstract Class 与 Interface 的构造

抽象类 Abstract Class

<?php
abstract class A {
abstract public function method1();
abstract public function method2();
public function method3() {
//... code ...
}
}
?>

接口 Interface

<?php
interface B {
public function method4();
public function method5();
}
?>

可以看到,Abstract Class 中可以有抽象函数(method1,method2),也可以有具体的函数实现(method3),而 Interface 中只能定义函数而不能有实现(method4,method5)。显然,如果一个抽象类中的函数全部是抽象函数,那么这个抽象类就退化成了接口。不过要注意的是

  • PHP 和 Java 一样,一个 Class 只能继承一个 Abstract Class,但可以实现多个 Interface。这即是所谓的单一继承体系,也就是子类别只能继承一个父类别;一个父类别则可以被多个子类别所继承。据说在 Python 中是可以多重继承的。
  • 在 Abstract Class 中可以声明属性成员变量(attribute),而 Interface 不可以。

二、举例

下面的例子可以帮助我们从另一个层面:Abstract Class 和 Interface 所反映出的设计理念,来分析一下二者的本质区别。

假设我们要定义一个关于“门”的 Class,门有“开”和“关”两个动作。此时我们既可以用 Abstract Class 也可以用 Interface 来描述这个抽象概念——

抽象类 Abstract Class

<?php
abstract class Door {
abstract public function open();
abstract public function close();
}
?>

接口 Interface

<?php
interface Door {
public function open();
public function close();
}
?>

在 Abstract Class 中并没有实现开门和关门方法,因为有的门可以用钥匙打开,有的门用密码打开,还有的门可以用指纹打开,这些具体方法就交给子类继承去实现。在这样看上去 Abstract Class 和 Interface 二者的使用没有太大差别。

现在如果定义一种具有“报警”功能的门,Abstract Class 和 Interface 描述类似——

抽象类 Abstract Class

<?php
abstract class Door {
abstract public function open();
abstract public function close();
abstract public function alarm();
}
class AlarmDoor extends Door {
public function open() {
//...
}
public function close() {
//...
}
public function alarm() {
//...
}
}
?>

接口 Interface

<?php
interface Door
{
public function open();
public function close();
public function alerm();
}
class AlarmDoor implements Door {
public function open() {
//...
}
public function close() {
//...
}
public function alarm() {
//...
}
}
?>

很明显上面的做法是不对的。它在定义中把 Door 本身固有的开门、关门的行为方法和另外一个概念“报警器”的行为方法混在了一起。有的门可以报警,而有的门可能没有报警功能;而会报警的门也可能多种多样(钥匙开门,刷卡开门等等)。我们必须将报警行为单独定义到另一个对象中,那么就有 3 种方式:

  • 门和报警器都用 abstract class 方式定义;
  • 门和报警器都用 interface 方式定义;
  • 一个使用 abstract class 方式定义,另一个使用 interface 方式定义。

由于 PHP 中子类只可继承自一个 Abstract Class,而且 Abstract Class 不支持多重继承(abstract class cannot extend abstract class),所以第一种方式显然不行。而第二种方式则没有能够正确的揭示我们的设计意图,也就是没有反映出 AlarmDoor 在概念本质上是 Door,同时它有具有报警功能。所以对于 Door 这个概念,我们应该采取 Abstract Class 方式来定义,AlarmDoor 继承自 Door,而报警概念通过 Interface 方式定义。

<?php
abstract class Door {
abstract function open();
abstract function close();
}
interface Alarm {
function alarm();
}
class AlarmDoor extends Door implements Alarm {
public function open() {
//...
}
public function close() {
//...
}
public function alarm() {
//...
}
}
?>

这样的实现基本上正确地反映了我们的设计意图。接口是可以多重继承的,子类也可以实现多个接口。这样就使得我们可以继续扩展门的功能,比如给门再装个摄像头……

三、结论

当我们使用 Abstract Class 的时候,我们应该定义一类对象的属性,即描述它 是什么。而 Interface 则类似于插件,侧重于提供附加的能力,约定它 能做什么

php Abstract 抽象类 与 Interface的的更多相关文章

  1. Java abstract 抽象类 和interface接口的异同点

    abstract 抽象类 和interface接口的异同点 相同点: 抽象类和接口都不能实例化,他们都位于继承树顶端,被其他类实现和继承 都可以包含抽象方法,实现接口或者继承抽象类的非抽象类(普通类) ...

  2. abstract抽象类和interface接口

    一.抽象类 1.抽象类不能实例化,因为有抽象方法未实现 2.可以被抽象类或非抽象类继承 3.但不是只能被继承,还可以直接拿来使用的,当然,这个使用是拿来声明,反例如下: public abstract ...

  3. Java:抽象类abstract和接口Interface

    一.抽象类:abstract 抽象类就是为了继承而存在的,如果你定义了一个抽象类,却不去继承它,那么等于白白创建了这个抽象类,因为你不能用它来做任何事情.对于一个父类,如果它的某个方法在父类中实现出来 ...

  4. JAVA的abstract修饰符 && 接口interface用法 && 抽象类和interface的差别

    abstract修饰符可以修饰类和方法. (1)abstract修饰类,会使这个类成为一个抽象类,这个类将不能生成对象实例,但可以做为对象变量声明的类型(见后面实例),也就是编译时类型.抽象类就相当于 ...

  5. 《Beginning Java 7》 - 7 - abstract class 抽象类 和 interface 接口

    1. 抽象类: 为什么用抽象类: 一些 generic 的类本身并没有现实意义,所以不需要被实例化.比如动物,自然界没有动物这个物种,但却有无数的继承自动物的物种,那么动物本身可以是一个抽象类. 抽象 ...

  6. Java abstract class 和 interface 的区别

    Java abstract class 和 interface 的区别 1. abstract class和interface是Java语言中对于抽象类定义进行支持的两种机制 2. 以Door的抽象概 ...

  7. 深入理解abstract class和interface(转)

    原文地址 深入理解abstract class和interface java提高篇(四)-----抽象类与接口

  8. abstract class和interface 知多少!!!

    1.相同点   A. 两者都是抽象类,都不能实例化.   B. interface实现类及abstrct class的子类都必须要实现已经声明的抽象方法. 2. 不同点   A. interface需 ...

  9. 11)Java abstract class 和 interface

    abstract class 和 interface 的区别        含有abstract修饰符的class即为抽象类,abstract 类不能创建实例对象.含有abstract方法的类必须定义 ...

随机推荐

  1. [刘阳Java]_为什么要前后端分离

    前后端分离已成为互联网项目开发的业界标准使用方式,通过nginx+tomcat的方式(也可以中间加一个nodejs)有效的进行解耦,并且前后端分离会为以后的大型分布式架构.弹性计算架构.微服务架构.多 ...

  2. [刘阳Java]_什么是EasyUI_第1讲

    jQuery EasyUI在Java后台开发中用得还是比较多.当然客观来讲虽然前端技术的发展,很多后台界面设计都植入了前端技术的框架.但是这篇文章我个人觉得也不会妨碍我们对jQuery EasyUI的 ...

  3. 如何快速更新长缓存的 HTTP 资源

    前言 HTTP 缓存时间一直让开发者头疼.时间太短,性能不够好:时间太长,更新不及时.当遇到严重问题需紧急修复时,尽管后端文件可快速替换,但前端文件仍从本地缓存加载,导致更新长时间无法生效. 对于这个 ...

  4. 将已经基本完成的项目推送到gitee上管理

    1,首先在gitee上创建仓库,保留一个.gitigonore就行,这样就得到一个远程仓库地址 2,仓库有文件,那么就需要先将这个文件pull下来: 在本地的项目目录中执行:git init git ...

  5. 原来ReadWriteLock也能开发高性能缓存,看完我也能和面试官好好聊聊了!

    大家好,我是冰河~~ 在实际工作中,有一种非常普遍的并发场景:那就是读多写少的场景.在这种场景下,为了优化程序的性能,我们经常使用缓存来提高应用的访问性能.因为缓存非常适合使用在读多写少的场景中.而在 ...

  6. go反射原理

    go反射原理 本文基于go1.13.15 1.go汇编 1.1 基本语法 go采用plan9的汇编器完成汇编,有下面几个重要的伪寄存器 FP: Frame pointer: 局部变量访问 PC: Pr ...

  7. P1447能量采集

    P1447能量采集 定义:(i,j)表示处于(i,j)的植物的贡献 我们发现,点(i,j)与(0,0)的连线所过整点的数目为\(\gcd(i,j)\) 发现要是想记录每个点的答案并不好算.那么怎么好算 ...

  8. BOM(Bill of Material)物料清单基础知识(一)

                                                                                            一.BOM的基础概念 概 ...

  9. odoo时间过滤

    Odoo中本日.本月.上月过滤器实现方法   Odoo中本日.本月.上月过滤器实现方法<filter string="今日订单" name="today" ...

  10. Nginx负载均衡反向代理服务器

    1.第一步先在IIS中创建多个网站,分别用不同的端口号.这里创建两个网站端口号分别8084.8085,在Nginx配置中会用到.测试两个网站能正常访问. 2.配置Nginx 1)增加负载均衡请求列表 ...