Definition

PHP接口的定义

什么是 PHP 接口呢,先来看下官方的解释:

接口是两个PHP对象之间的 契约,其目的是不让一个对象依赖另一个对象的 身份,而是依赖另一个对象的 能力

听起来一头雾水吧?我给大家举个例子:

比如我昨天丢了一个手机,想买一部新的,于是来到一家手机专卖店。在这里有很多种手机供我选择:诺基亚(老年人专用),小米(中端配置),三星S8和 Vertu(可以说是全球最贵的手机了,不了解的同学可以去百度)。而我想实现的功能其实就是打电话和发微信,现实情况是,以上四种手机都可以满足我的需求,唯一不同的是价格差距。诺基亚可以,但是我不是老年人,不需要那么大的字体和臃肿的外观;Vertu 当然买不起;小米也还行,但是我想要更大点的屏幕和更好的性能,最终我选择了三星S8。

其实,这四种手机我都可以用,因为他们实现了相同的接口:打电话,发微信,而且消耗的能源都是电。如果我每天从180平米的床上醒来的话,我当然会选择 Vertu 。(老实说,我还是会选择三星S8)

在 PHP 中,道理完全一样。如果我编写的代码要处理指定类的对象(从而要使用特殊的方法实现),那么代码的功用就完全被限定了,因为我只能使用那个类的方法。可是,如果我要编写的代码是一个接口,那么代码就能立即知道如何实现处理这一接口的任何对象。这就像我们在使用手机的时候,不必关心手机内部零部件是什么样的,硬件和软件之间的通信流程是什么样的,我们只要知道他能够打电话发微信就行了。在写代码的时候也是一样,我们不需要知道我们要用的接口是如何实现功能的,我们只需要知道接口能不能实现我们需要的功能。

define and use

声明和使用

我将举一个我实际工作中的示例,抽象为一个例子,讲解给大家:

比如我收到了一个需求,这个需求的具体要求是获取在加油站油罐中液位仪的数据,(你可以理解液位仪就是在油罐中的一根探棒,用来获取油罐中储油的体积、温度、密度、油高等数据)而公司提供了三种途径来这些数据。

我先定义一个存储获取数据的处理类:


<?php
class levelMeterStore
{
    protected $data = [];

    // 更新液位仪数据
    public function updateLevelData(Meterable $levelMeter)
    {
        $meter_id = $levelMeter->getId();
        $meter_data = $levelMeter->getData();
        $this->data[$meter_id] = $meter_data;
    }

    // 获取液位仪数据
    public function getLevelMeters()
    {
        return $this->data;
    }
}
		

既然 updateLevelData() 方法的参数只能是 Meterable 的实例,这样定义 levelMeterStore 类怎么能行呢?仔细观察,其实, Meterable 不是类,而是一个接口,我会这样定义:


interface Meterable
{
    public function getId();
    public function getData();
}
		

这个接口定义表明:实现 Meterable 接口的任何对象,都必须有两个对外公开的 getId()getData() 方法。

可是这么做到底有什么用呢?这么做的用处是,我们可以分开定义获取液位仪数据的类,上文说道,公司会提供三种不同的方式来获取液位仪的数据。所以我就能利用接口来实现这三种不同的方式。以下是三种方式的示例代码:

方案一:直接去取液位仪的数据

这个方式很简单,就是直接获取到液位仪的十六进制数据,再本地做对应的解析后入库:


class DirectLeveData implements Meterable
{
    protected $data;

    public function __construct($data)
    {
        // 这里接收到的是十六进制数据
        $this->data = $this->deCode($data);
    }

    public function getId()
    {
        return $this->data->id;
    }

    public function getData()
    {
        return $this->data->content;
    }

    private function deCode()
    {
        foreach ($this->data as &$value) {
            // 将十六进制转换为十进制
            $value = hexdec($value);
            // 其他处理 ...
        }
    }
}
		
方案二:调用硬件部门的大牛脚本 获取解析过的数据

有些数据过于复杂,我自己也无能为力,此时公司提供了一个硬件部门大牛写的解析脚本,我只需要调用接口就可以返回解析过的数据了,代码实现如下:


class LazyLeveData implements Meterable
{
    protected $url;
    protected $data;

    public function __construct($url)
    {
        // 获取需要解析的接口地址
        $this->url = $url;
        $this->parseUrl();
    }

    public function getId()
    {
        return $this->data->id;
    }

    public function getData()
    {
        return $this->data->content;
    }

    // 调用接口获取数据
    private function parseUrl()
    {
        curl_setopt($ch, CURLOPT_URL, $this->url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
        curl_setopt($ch, CURLOPT_TIMEOUT, 5);
        $data = curl_exec($ch);
        curl_close($ch);

        $this->data = $data;
    }
}
		
方案三:使用 Kafka 队列,获取队列中的数据

为了进一步提升性能,公司要求有些数据要通过队列方式获取,这就是第三种方式:


use Kafka;

class KafkaLeveData implements Meterable
{
    protected $pop_id;
    protected $data;

    public function __construct($pop_id)
    {
        // 获取需要解析的接口地址
        $this->pop_id = $pop_id;
        $this->getKafkaData();
    }

    public function getId()
    {
        return $this->data->id;
    }

    public function getData()
    {
        return $this->data->content;
    }

    // 调用接口获取数据
    private function getKafkaData()
    {
        $kafka = new Kafka();
        // 省略验证连接等流程
        $data = $kafka->getData($this->pop_id);

        $this->data = $data;
    }
}
		

现在就方便啦,我可以判断数据来源,直接调用这三种实现了接口的类,就可以轻易的获取到液位仪的数据:


<?php
$levelMeterStore = new levelMeterStore();

// 直接去取液位仪的数据
$directData = new DirectLeveData($data);
$levelMeterStore->updateLevelData($directData);

// 调用硬件部门的大牛脚本
$LazyData = new LazyLeveData('http://***.***.com/api.php');
$levelMeterStore->updateLevelData($LazyData);

// 使用 Kafka 队列
$KafkaData = new KafkaLeveData(1099232);
$levelMeterStore->updateLevelData($KafkaData);

// 列出所有液位仪数据
print_r($levelMeterStore->getLevelMeters());
		

你们看,是不是很简洁?我使用了三个类,但是这三个类之间毫无关联,只是都实现了同样的接口。

总之,使用接口编程,写出的代码将会变得更加灵活,在团队开发过程中,可以省去很多协调和沟通的时间,因为别人只需要知道你有没有实现了他们要求的接口,就可以无缝的结合使用你的代码。

请登录

WOWPHP 账号登录 GitHub 账号登录

还没有账号?现在去注册一个~