zoukankan      html  css  js  c++  java
  • 说说接口封装

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

    函数名

    首先是函数名。函数名的好坏很明显,我的观点,是否简单,不重复。比如在一个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 <span class="hljs-title">Exception{

    }

    function insertUserByField($name, $code, $age) {
    $db = db::connect();
    if(empty($name) || empty($code) || empty($age)) {
    throw new UserException(PARAM_ERROR);
    }

    <span class="hljs-keyword">if</span> ($age &gt; <span class="hljs-number">15</span>) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> UserException(AGE_TOO_BIG);
    }
    
    $ret = $db-&gt;insert(<span class="hljs-string">'user'</span>)-&gt;create(compact(<span class="hljs-string">'name'</span>, <span class="hljs-string">'code'</span>, <span class="hljs-string">'page'</span>));
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">empty</span>($ret)) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> UserException(INSERT_ERROR);
    }
    <span class="hljs-keyword">return</span> $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(<span class="hljs-keyword">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];
    }

        <span class="hljs-keyword">if</span> ($age &gt; <span class="hljs-number">15</span>) {
            <span class="hljs-keyword">return</span> [AGE_TOO_BIG, <span class="hljs-keyword">null</span>];
        }
    
        $ret = $db-&gt;insert(<span class="hljs-string">'user'</span>)-&gt;create(compact(<span class="hljs-string">'name'</span>, <span class="hljs-string">'code'</span>, <span class="hljs-string">'page'</span>));
        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">empty</span>($ret)) {
            <span class="hljs-keyword">return</span> [INSERT_ERROR, <span class="hljs-keyword">null</span>];
        }
        <span class="hljs-keyword">return</span> [OK, $ret];
    } <span class="hljs-keyword">catch</span> (<span class="hljs-keyword">Exception</span> $e) {
        <span class="hljs-comment">// do log</span>
        <span class="hljs-keyword">return</span> [INNNER_ERROR, <span class="hljs-keyword">null</span>];
    }
    

    }

    // 使用
    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];
    }

    <span class="hljs-keyword">if</span> ($age &gt; <span class="hljs-number">15</span>) {
        <span class="hljs-keyword">return</span> [AGE_TOO_BIG, <span class="hljs-keyword">null</span>];
    }
    
    $ret = $db-&gt;insert(<span class="hljs-string">'user'</span>)-&gt;create(compact(<span class="hljs-string">'name'</span>, <span class="hljs-string">'code'</span>, <span class="hljs-string">'page'</span>));
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">empty</span>($ret)) {
        <span class="hljs-keyword">return</span> [INSERT_ERROR, <span class="hljs-keyword">null</span>];
    }
    <span class="hljs-keyword">return</span> [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 异常是默认出错,在错误中找“可修复”的错误。错误码是默认正常,在正常中找“可修复”的错误。前者更为保守。

  • 相关阅读:
    网线
    第19次实验
    矩阵乘法
    20次试验
    视频笔记
    1
    effective C++ 条款 34:区分接口继承和实现继承
    effective C++ 条款 35:考虑virtual函数以外的其他选择
    effective C++ 条款 29:为“异常安全”而努力是值得的
    effective C++ 条款 27:尽量少做转型动作
  • 原文地址:https://www.cnblogs.com/jpfss/p/9294304.html
Copyright © 2011-2022 走看看