• 说说接口封装


    今天给同事封装了一个接口,说起接口封装的事情,其实其实很有的聊。很多时候,说一个服务好,一个服务烂,实际上都是在吐槽服务队外暴露的接口好坏。不管什么语言,封装接口,抽象起来,就是由一个函数名,若干个参数,若干个返回值组成的。封装的好坏,就在这几个上面。

    函数名

    首先是函数名。函数名的好坏很明显,我的观点,是否简单,不重复。比如在一个User类中你封装一个方法,叫做findUser。我就觉得很啰嗦了。你使用的时候会这样使用

    User::findUser($id);
    

    那又是何必呢?为什不直接叫做find呢?

    User::find($id);
    

    我记得前段时间在网上还看到一篇文章,你见过哪些奇葩的代码。其中就有一些有趣的函数名。在我的视角看来,下面的函数名都很奇葩:

    function weizhi() // 中文拼音
    
    function getuserinfo() // 单词和单词没用大小写分割
    
    function getUserIsEnable() // 明明是bool判断却用get开头
    

    基本上,我们选择使用 动词 或者 动词+名词 或者 动词+名词 + 副词

    比如

    function find()
    
    function getUser()
    
    function getUserByName()
    

    我觉得这些都是很符合人性的函数名。

    参数

    一句话, 参数尽量不要封装。。。尽量不要太多。。。

    尽量不要封装就是,能队外暴露的细节越多,用户使用成本越低,比如,根据地理位置获取地址的函数

    // 里面的$coord是一个数组['lat','lng']
    function getCityByCoord($coord)
    

    就不如

    function getCityByCoord($lat, $lng)
    

    还有不要太多就是如果你参数个数超过5个,就该考虑封装了。封装的时候,我习惯会把一些“不重要的”,“不常用的”封装成一个参数,并且设置这个参数默认值。

    // 这里的conditions 可以设置表列名,只能用等号 ['class' => 1]
    function getUsers($offset, $limit, $sort, $conditions = [])
    

    返回值

    这个返回值就很有的说了。首先遇到的问题是,返回值是否是返回数组。这个问题让我想起了在刚接触php的时候,那时候刚从c#转过来,对接手的项目的一个函数返回值包含什么一直不理解。问了同事,他的回复是,你调用一下就可以知道他们返回什么了。

    反正吧,对于php的返回值,我的观点就是,如果你的项目在追求的是快,而且开发人数也不多,那么,你就可以使用数组来做交互。如果你的项目追求的是工程化,模块和模块之间的交互需要人与人的沟通,那么,尽量定义好对象。使用对象进行交互。事实上,像laravel这类追求工程化的框架,你在实现和别人交互的接口的时候,尽量传递的是Model,或者Collection比较好。

    比如

    // in Service
    
    // return: LocationModel
    public function findByName($name){
        return LocationModel::where('name', trim($name))->first();
    }
    

    异常和错误

    接口函数定义好了,可不是就结束了,这个函数是否会抛出异常?是否会返回错误?

    对于错误和异常的理解,我的理解是:

    • 异常是不能被兼容处理的
    • 错误是希望被兼容处理的

    我记得在上一个项目,我强烈建议团队小伙伴们在封装对外的soa的sdk的时候,使用的方式是如此:

    list($code, $data) = UserService::getUserByName($name);
    if ($code) {
        // 处理对应的错误
    }
    

    php原本的返回值只有一个对象,这里使用list拆成两个对象,一个是code,代表返回值的错误信息,一个是data,代表如果没有错误的话,返回的结构。这个接口,我们在内部做了try_catch,不会抛出异常,所有的信息都以错误码的形式返回。

    如果在内部try_catch捕获到了异常,则返回的code会是500。

    其实使用异常还是错误码处理错误都是可以的。错误码是写程序的时候最早使用的方法,但是异常机制出现后,各个语言都倾向于使用异常处理错误了。

    就php而言,是建议使用异常处理的。它本身的内部也定义了一堆的build-in 异常。

    • BadFunctionCallException
    • BadMethodCallException
    • DomainException
    • InvalidArgumentException
    • LengthException
    • LogicException
    • OutOfBoundsException
    • OutOfRangeException
    • OverflowException
    • RangeException
    • RuntimeException
    • UnderflowException
    • UnexpectedValueException

    体会下下面两段代码,分别使用异常和错误码处理

    const PARAM_ERROR = 100;
    const AGE_TOO_BIG = 200;
    const INSERT_ERROR = 300;
    
    class UserException extends Exception{
    
    }
    
    function insertUserByField($name, $code, $age) {
        $db = db::connect();
        if(empty($name) || empty($code) || empty($age)) {
            throw new UserException(PARAM_ERROR);
        }
    
        if ($age > 15) {
            throw new UserException(AGE_TOO_BIG);
        }
    
        $ret = $db->insert('user')->create(compact('name', 'code', 'page'));
        if (empty($ret)) {
            throw new UserException(INSERT_ERROR);
        }
        return $ret;
    }
    
    // 使用
    try{
        $user = $userService->insertUserByField('foo', 291212, 34);
    } catch(UserException $e) {
        switch($e->getCode()):
            case PARAM_ERROR:
            //
            case AGE_TOO_BIG:
            //
            case INSERT_ERROR:
            //
            default:
            //
    } catch(Exception $e) {
        //
    }
    

    const OK = 500;
    
    const PARAM_ERROR = 100;
    const AGE_TOO_BIG = 200;
    const INSERT_ERROR = 300;
    
    const INNNER_ERROR = 500;
    
    function insertUserByField($name, $code, $age) {
        try {
            $db = db::connect();
            if(empty($name) || empty($code) || empty($age)) {
                return [PARAM_ERROR, null];
            }
    
            if ($age > 15) {
                return [AGE_TOO_BIG, null];
            }
    
            $ret = $db->insert('user')->create(compact('name', 'code', 'page'));
            if (empty($ret)) {
                return [INSERT_ERROR, null];
            }
            return [OK, $ret];
        } catch (Exception $e) {
            // do log
            return [INNNER_ERROR, null];
        }
    }
    
    // 使用
    list($code, $user) = $userService->insertUserByField('foo', 291212, 34);
    if ($code) {
        switch $code {
            case PARAM_ERROR:
            //
            case AGE_TOO_BIG:
            //
            case INSERT_ERROR:
            //
            default:
            //
        }
    }
    

    我认为,golang中的错误处理机制给了我们很好的示范。它有个error机制代表错误,panic机制代表异常。

    func getUserByName(name string) (int, error) {
        if len(name) == 0 {
            return 0, errors.New("param error")
        }
        //
    }
    
    data, err := getUserByName(name)
    if err != nil {
        ....
    }
    

    这里的err代表getUserByName的时候有可能返回错误。它也是期望(甚至于强制)调用方处理各种error。但是它并不保证这个函数不会发生panic,一旦发生panic,整个系统也会崩溃。你需要使用recover来捕获。

    如果把golang的这种做法应用在php中,上面的例子可能就会变成:

    const OK = 500;
    
    const PARAM_ERROR = 100;
    const AGE_TOO_BIG = 200;
    const INSERT_ERROR = 300;
    
    const INNNER_ERROR = 500;
    
    // 这里对可能出现的exception就不需要管了,只处理希望上层处理的“错误”
    function insertUserByField($name, $code, $age) {
        $db = db::connect();
        if(empty($name) || empty($code) || empty($age)) {
            return [PARAM_ERROR, null];
        }
    
        if ($age > 15) {
            return [AGE_TOO_BIG, null];
        }
    
        $ret = $db->insert('user')->create(compact('name', 'code', 'page'));
        if (empty($ret)) {
            return [INSERT_ERROR, null];
        }
        return [OK, $ret];
    }
    
    // 如果你有框架的话,这里的try catch就可以在框架统一捕获了。
    list($code, $user) = $userService->insertUserByField('foo', 291212, 34);
    if ($code) {
        switch $code {
            case PARAM_ERROR:
            //
            case AGE_TOO_BIG:
            //
            case INSERT_ERROR:
            //
            default:
            //
        }
    }
    
    

    关于异常和错误这块,不同的语言,不同的人有不同的使用习惯,我的看法,golang中对异常和错误的处理机制是最好的。将两者分别对待。
    但是在php中,如果需要有个“银弹”说法的话:尽量使用异常来处理。

    如果硬要问为什么?基本上,有两个原因:

    1 异常的堆栈信息比错误码丰富
    2 异常是默认出错,在错误中找“可修复”的错误。错误码是默认正常,在正常中找“可修复”的错误。前者更为保守。

  • 相关阅读:
    ASP.NET 实现验证码以及刷新验证码
    使用纯生js操作cookie
    TesseractOCR Tutorials
    c# 解析JSON的几种办法
    ElasticSearch常用查询命令-kibana中使用
    ElasticSearch集成IK分词器
    Typora使用教程 之 PicGo集成做图床
    Kibana-CentOS7单机安装测试
    Elasticsearch-CentOS7单机安装测试
    CentOS7安装JDK8
  • 原文地址:https://www.cnblogs.com/yjf512/p/6525970.html
Copyright © 2020-2023  润新知