PHP抽象类和对象接口

对象接口
使用接口(interface),可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。
接口是通过 interface 关键字来定义的,就像定义一个标准的类一样,但其中定义所有的方法都是空的。
接口中定义的所有方法都必须是公有,这是接口的特性。
要实现一个接口,使用 implements 操作符。类中必须实现接口中定义的所有方法,否则会报一个致命错误。类可以实现多个接口,用逗号来分隔多个接口的名称。接口可以继承其他接口

从宏观上理解,接口更像是某种功能的对接,如果某个类需要实现某项公用的功能,就实现这个接口中的方法。在多个类通过不同方式实现同一功能的时候,使用者仅需要每个接口中方法的含义和使用方法,而不需要去关心每个不同类中的实现过程。

例1:
定义一个canfly接口,一个canswim接口,一个bird类。
每种鸟都继承自bird类,
dove不会游泳会飞,则只实现 canfly接口
penguin不会飞会游泳,则只实现  canswim接口
duck既会飞又会游泳,则实现  canfly 和 canswim 两个接口
<?php

interface CanFly {
  public function fly();
}

interface CanSwim {
  public function swim();
}

class Bird {
  public function info() {
    echo "I am a {$this->name}\n";
    echo "I am an bird\n";
  }
}


class Dove extends Bird implements CanFly {
  var $name = "Dove";
  public function fly() {
    echo "I fly\n";
  } 
}

class Penguin extends Bird implements CanSwim {
  var $name = "Penguin";
  public function swim() {
    echo "I swim\n";
  } 
}

class Duck extends Bird implements CanFly, CanSwim {
  var $name = "Duck";
  public function fly() {
    echo "I fly\n";
  }
  public function swim() {
    echo "I swim\n";
  }
}


例2:
在实际的使用中,对不同数据库的操作,是一个很好的例子:
要操作一个数据库的话,定义一个接口如下
interface Database {
function listOrders();
function addOrder();
function removeOrder();
...
}

如果操作的是MySQL数据库的话,操作类可以这样写
class MySqlDatabase implements Database {
function listOrders() {...
}

操作MySQL数据库的话,可以这样:
$database = new MySqlDatabase();
foreach ($database->listOrders() as $order) {

如果转而使用Oracle数据库的话,我们可以另写一个类:
class OracleDatabase implements Database {
public function listOrders() {...
}

当我们需要操作Oracle数据库的时候,仅需要更改new的类名,而不需要进行其他操作
$database = new OracleDatabase();
foreach ($database->listOrders() as $order) {

抽象类
PHP 5 支持抽象类和抽象方法。定义为抽象的类不能被实例化。任何一个类,如果它里面至少有一个方法是被声明为抽象的,那么这个类就必须被声明为抽象的。被定义为抽象的方法只是声明了其调用方式(参数),不能定义其具体的功能实现。
继承一个抽象类的时候,子类必须定义父类中的所有抽象方法;另外,这些方法的访问控制必须和父类中一样(或者更为宽松)。例如某个抽象方法被声明为受保护的,那么子类中实现的方法就应该声明为受保护的或者公有的,而不能定义为私有的。此外方法的调用方式必须匹配,即类型和所需参数数量必须一致。例如,子类定义了一个可选参数,而父类抽象方法的声明里没有,则两者的声明并无冲突。 这也适用于 PHP 5.4 起的构造函数。在 PHP 5.4 之前的构造函数声明可以不一样的。




两者对比:
从对抽象类的介绍中可以发现,抽象类和接口有很多共同点,比如不能被实例化,被抽象的方法在子类中必须被定义,被抽象的子类不能定义其具体功能的实现等。很容易把接口和抽象类混淆在一起,两者使用方法有些类似,但其实两者在功能上来说有本质的区别。

在PHP官方文档中,在一些评论中有很好的分析。  http://php.net/manual/zh/language.oop5.abstract.php

简单来说,接口更像是一种约定好的协议。某个类实现这个接口的话,那我不用关心类是怎么写的,我可以直接去操作这个接口里的方法。
抽象类,更像是一个没有完工的建筑,某个类继承抽象类,就是去完善这个建筑,只有建筑完成了,我们才知道这是一个具体的什么建筑。

You would have your class implement a particular interface if you were distributing a class to be used by other people. The interface is an agreement to have a specific set of public methods for your class.

You would have your class extend an abstract class if you (or someone else) wrote a class that already had some methods written that you want to use in your new class.

These concepts, while easy to confuse, are specifically different and distinct. For all intents and purposes, if you're the only user of any of your classes, you don't need to implement interfaces.

上面那个鸟类的例子,假如增加一个eat方法用来说明进食的东西,dove吃谷物,Penguin吃鱼,duck吃青菜,但是 bird吃什么却不能统一而论 ,因此,bird类中的eat方法可以写成抽象方法,bird类可以写成抽象类,

abstract class Bird {
  abstract protected function eat(){}
  public function info() {
    echo "I am a {$this->name}\n";
    echo "I am an bird\n";
  }
}

class Dove extends Bird implements CanFly {
  var $name = "Dove";
  public function eat(){
echo "我吃谷物";
  }

  public function fly() {
    echo "I fly\n";
  } 
}

class Penguin extends Bird implements CanSwim {
  var $name = "Penguin";
  public function eat(){
echo "我吃鱼";
  }
  public function swim() {
    echo "I swim\n";
  } 
}

class Duck extends Bird implements CanFly, CanSwim {
  var $name = "Duck";
  public function eat(){
echo "我吃青菜";
  }
  public function fly() {
    echo "I fly\n";
  }
  public function swim() {
    echo "I swim\n";
  }
}


function describe($bird) {
  if ($bird instanceof Bird) {
    $bird->eat();
    $bird->info();
    if ($bird instanceof CanFly) {
      $bird->fly();
    }
    if ($bird instanceof CanSwim) {
      $bird->swim();
    }
  } else {
    die("This is not a bird. I cannot describe it.");
  }
}

// describe these birds please
describe(new Penguin);
echo "---\n";

describe(new Dove);
echo "---\n";

describe(new Duck);
?>


运行结果:
我吃鱼
I am a Penguin
I am an bird
I swim
---
我吃谷物
I am a Dove
I am an bird
I fly
---
我吃青菜
I am a Duck
I am an bird
I fly
I swim

**********************************************************************
其他要点:
1、抽象类不可以直接被实例化,但抽象类中的静态方法可以直接调用
<?php
interface a
{
    const b = 'Interface constant';
}

// 输出接口常量
echo a::b;

// 错误写法,因为常量不能被覆盖。接口常量的概念和类常量是一样的。
class b implements a
{
    const b = 'Class constant';
}
?>

2、接口中也可以定义常量。接口常量和类常量的使用完全相同,但是不能被子类或子接口所覆盖。
<?php
interface a
{
    const b = 'Interface constant';
}

// 输出接口常量
echo a::b;

// 错误写法,因为常量不能被覆盖。接口常量的概念和类常量是一样的。
class b implements a
{
    const b = 'Class constant';
}
?>















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值