zoukankan      html  css  js  c++  java
  • 使用引用

    ### 使用引用

    **场景一:遍历一个数组获取新的数据结构**

    也许你会这样写:

    ```
    // 申明一个新的数组,组装成你想要的数据
    $tmp = [];
    foreach ($arr as $k => $v) {
        // 取出你想要的数据
        $tmp[$k]['youwant'] = $v['youwant'];
        ...
        // 一系列判断得到你想要的数据
        if (...) {
            $tmp[$k]['youwantbyjudge'] = 'TIGERB';
        }
        ...
    }
    // 最后得要你想要的数组$tmp

    -------------------------------------------------------

    // 也许你觉着上面的写法不是很好,那我们下面换种写法
    foreach ($arr as $k => $v) {
        // 一系列判断得到你想要的数据
        if (...) {
            // 复写值为你想要的
            $arr[$k]['youwantbyjudge'] = 'TIGERB'
        }
        ...
        // 干掉你不想要的结构
        unset($arr[$k]['youwantdel']);
    }
    // 最后我们得到我们的目标数组$arr
    ```


    接下来我们使用引用值:

    ```
    foreach ($arr as &$v) {
        // 一系列判断得到你想要的数据
        if (...) {
            // 复写值为你想要的
            $v['youwantbyjudge'] = 'TIGERB'
        }
        ...
        // 干掉你不想要的结构
        unset($v['youwantdel']);
    }
    unset($v);
    // 最后我们得到我们的目标数组$arr
    ```

    使用引用是不是使我们的代码更加的简洁,除此之外相对于第一种写法,我们节省了内存空间,尤其是再操作一个大数组时效果是及其明显的。

    **场景二:传递一个值到一个函数中获取新的值**

    基本和数组遍历一致,我们只需要声明这个函数的这个参数为引用即可,如下:

    ```
    function decorate(&$arr = []) {
        # code...
    }

    $arr = [
        ....
    ];
    // 调用函数
    decorate($arr);
    // 如上即得到新的值$arr,好处还是节省内存空间

    ```

    ### 使用try...catch...

    假如有下面一段逻辑:
    ```
    class UserModel
    {
        public function login($username = '', $password = '')
        {
            code...
            if (...) {
                // 用户不存在
                return -1;
            }
            code...
            if (...) {
                // 密码错误
                return -2;
            }
            code...
        }
    }

    class UserController
    {
        public function login($username = '', $password = '')
        {
            $model = new UserModel();
            $res   = $model->login($username, $password);
            if ($res === -1) {
                return [
                    'code' => '404',
                    'message' => '用户不存在'
                ];
            }
            if ($res === -2) {
                return [
                    'code' => '400',
                    'message' => '密码错误'
                ];
            }
            code...
        }
    }
    ```

    我们用try...catch...改写后:
    ```
    class UserModel
    {
        public function login($username = '', $password = '')
        {
            code...
            if (...) {
                // 用户不存在
                throw new Exception('用户不存在', '404');
            }
            code...
            if (...) {
                // 密码错误
                throw new Exception('密码错误', '400');
            }
            code...
        }
    }

    class UserController
    {
        public function login($username = '', $password = '')
        {
            try {
                $model = new UserModel();
                $res   = $model->login($username, $password);
                // 如果需要的话,我们可以在这里统一commit数据库事务
                // $db->commit();
            } catch (Exception $e) {
                // 如果需要的话,我们可以在这里统一rollback数据库事务
                // $db->rollback();
                return [
                    'code'    => $e->getCode(),
                    'message' => $e->getMessage()
                ]
            }
        }
    }
    ```

    通过使用try...catch...使我们的代码逻辑更加清晰,try...里只需要关注业务正常的情况,异常的处理统一在catch中。所以,我们在写上游代码时异常直接抛出即可。

    ### 使用匿名函数

    ** 构建函数或方法内部的代码块 **

    假如我们有一段逻辑,在一个函数或者方法里我们需要格式化数据,但是这个格式化数据的代码片段出现了多次,如果我们直接写可能会想下面这样:

    ```
    function doSomething(...) {
        ...
        // 格式化代码段
        ...
        ...
        // 格式化代码段[重复的代码]
        ...
    }
    ```

    我相信大多数的人应该不会像上面这么写,可能都会像下面这样:

    ```
    function doSomething(...) {
        ...
        format(...);
        ...
        format(...);
        ...
    }

    // 再声明一个格式花代码的函数或方法
    function format() {
        // 格式化代码段
        ...
    }
    ```

    上面这样的写法没有任何的问题,最小单元化我们的代码片段,但是如果这个format函数或者方法只是doSomething使用呢?我通常会像下面这么写,为什么?因为我认为在这种上下文的环境中format和doSomething的一个子集。

    ```
    function doSomething() {
        ...
        $package = function (...) use (...) { // 同样use后面的参数也可以传引用
            // 格式化代码段
            ...
        };
        ...
        package(...);
        ...
        package(...);
        ...
    }
    ```

    ** 实现类的【懒加载】和实现设计模式的【最少知道原则】 **

    假如有下面这段代码:

    ```
    class One
    {
        private $instance;

        // 类One内部依赖了类Two
        // 不符合设计模式的最少知道原则
        public function __construct()
        {  
            $this->intance = new Two();
        }

        public function doSomething()
        {
            if (...) {
                // 如果某种情况调用类Two的实例方法
                $this->instance->do(...);
            }
            ...
        }
    }
    ...

    $instance = new One();
    $instance->doSomething();
    ...
    ```

    上面的写法有什么问题?

    - 不符合设计模式的最少知道原则,类One内部直接依赖了类Two
    - 类Two的实例不是所有的上下文都会用到,所以浪费了资源,有人说搞个单例,但是解决不了实例化了不用的尴尬

    所以我们使用匿名函数解决上面的问题,下面我们这么改写:

    ```
    class One
    {
        private $closure;

        public function __construct(Closure $closure)
        {  
            $this->closure = $closure;
        }

        public function doSomething()
        {
            if (...) {
                // 用的时候再实例化
                // 实现懒加载
                $instance = $this->closure();
                $instance->do(...)
            }
            ...
        }
    }
    ...

    $instance = new One(function () {
        // 类One外部依赖了类Two
        return new Two();
    });
    $instance->doSomething();
    ...
    ```

    ### 减少对if...else...的使用

    如果你碰见下面这种类型的代码,那一定是个黑洞。

    ```
    function doSomething() {
        if (...) {
            if (...) {
                ...
            } esle {
                ...
            }
        } else {
            if (...) {
                ...
            } esle {
                ...
            }
        }
    }

    ```

    ** 提前return异常 **

    细心的你可能会发现上面这种情况,可能绝大多数else代码里都是在处理异常情况,更有可能这个异常代码特别简单,通常我会这么去做:

    ```
    // 如果是在一个函数里面我会先处理异常的情况,然后提前return代码,最后再执行正常的逻辑
    function doSomething() {
        if (...) {
            // 异常情况
            return ...;
        }
        if (...) {
            // 异常情况
            return ...;
        }
        // 正常逻辑
        ...
    }

    // 同样,如果是在一个类里面我会先处理异常的情况,然后先抛出异常
    class One
    {
        public function doSomething()
        {
            if (...) {
                // 异常情况
                throw new Exception(...);
            }
            if (...) {
                // 异常情况
                throw new Exception(...);
            }
            // 正常逻辑
            ...
        }
    }

    ```

    ** 关联数组做map **

    如果我们在客户端做决策,通常我们会判断不同的上下文在选择不同策略,通常会像下面一样使用if或者switch判断:

    ```
    class One
    {
        public function doSomething()
        {
            if (...) {
                $instance = new A();
            } elseif (...) {
                $instance = new A();
            } else {
                $instance = new C();
            }
            $instance->doSomething(...);
            ...
        }
    }
    ```

    上面的写法通常会出现大量的if语句或者switch语句,通常我会使用一个map来映射不同的策略,像下面这样:

    ```
    class One
    {
        private $map = [
            'a' => 'namespace\A', // 带上命名空间,因为变量是动态的
            'b' => 'namespace\B',
            'c' => 'namespace\C'
        ];
        public function doSomething()
        {
            ...
            $instance = new $this->map[$strategy];// $strategy是'a'或'b'或'c'
            $instance->doSomething(...);
            ...
        }
    }
    ```

    ### 使用接口

    为什么要使用接口?极大的便于后期的扩展和代码的可读性,例如设计一个优惠系统,不同的商品只是在不同的优惠策略下具备不同的优惠行为,我们定义一个优惠行为的接口,最后对这个接口编程即可,伪代码如下

    ```
    Interface Promotion
    {
        public function promote(...);
    }

    class OnePromotion implement Promotion
    {
        public function doSomething(...)
        {
            ...
        }
    }

    class TwoPromotion implement Promotion
    {
        public function doSomething(...)
        {
            ...
        }
    }
    ```

    ### 控制器拒绝直接的DB操作

    最后我想说的是永远拒绝在你的Controller里直接操作DB,为什么?我们的程序绝大多数的操作基本都是增删改查,可能是查询的where条件和字段不同,所以有时候我们可以抽象的把对数据库增删改查的方法写到model中,通过参数暴露我们的where,fields条件。通常这样可以很大程度的提高效率和代码复用。比如像下面这样:

    ```
    class DemoModel implement Model
    {
        public function getMultiDate($where = [], $fields = ['id'], $orderby = 'id asc')
        {
            $this->where($where)
                 ->field($fields)
                 ->orderby($orderby)
                 ->get();
        }
    }
    ```

    ### 最后

    如果有写的不对的地方,欢迎大家指正,THX~

    > [Easy PHP:一个极速轻量级的PHP全栈框架](http://php.tigerb.cn/)

  • 相关阅读:
    OpenCV中的霍夫线变换和霍夫圆变换
    霍夫圆变换
    异或的性质及运用
    不用中间变量交换两个数值变量的值
    图像变换
    STM32程序的启动
    RAM与FLASH
    STM8的AIR与STM32的Keil的指定地址存数据
    HEX与ASCII之间装换
    STM32、Cortex-A、Cortex-R、Cortex-M、SecurCore
  • 原文地址:https://www.cnblogs.com/lxwphp/p/15454912.html
Copyright © 2011-2022 走看看