PHPer может быть более элегантным, чтобы писать такой код

задняя часть база данных Шаблоны проектирования

предисловие

Прошло почти два года с тех пор, как я в мгновение ока стал PHP-менеджером, и за это время у меня появилось несколько идей о том, как писать код, который легко читается и легко расширяется.

Используйте ссылки

Сценарий 1. Обход массива для получения новой структуры данных

Может быть, вы бы написали это:

// 申明一个新的数组,组装成你想要的数据
$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

Делает ли использование ссылок наш код более лаконичным?Кроме того, по сравнению с первым способом записи, мы экономим место в памяти, особенно при работе с большим массивом, эффект очень очевиден.

Сценарий 2: передача значения функции для получения нового значения

В основном то же самое, что и обход массива, нам нужно только объявить этот параметр этой функции как ссылку следующим образом:

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

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

Используйте попробуйте ... поймать ...

Если есть логика ниже:

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() {
    // 格式化代码段
    ...
}

Нет никаких проблем с описанным выше способом написания и самой маленькой единицей нашего фрагмента кода, но что, если эта функция или метод формата используется только 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 {
            ...
        }
    }
}

** Исключение досрочного возврата **

Если вы будете внимательны, то можете столкнуться с описанной выше ситуацией. Возможно, большая часть кода else имеет дело с исключениями. Скорее всего, этот код исключения очень прост. Обычно я делаю так:

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

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

**Ассоциативный массив в виде карты **

Если мы принимаем решения на стороне клиента, мы обычно считаем, что разные контексты выбирают разные стратегии, обычно используя суждения if или switch следующим образом:

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

Приведенный выше метод написания обычно имеет большое количество операторов if или операторов switch, обычно я буду использовать карту для сопоставления различных стратегий, таких как следующие:

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(...)
    {
        ...
    }
}

Контроллер отклоняет прямые операции с БД

Последнее, что я хочу сказать, это всегда отказываться от работы с БД напрямую в вашем контроллере, почему? Большинство операций нашей программы в основном добавляются, удаляются, модифицируются и проверяются.Возможно, где условия и поля запроса отличаются, поэтому иногда мы можем абстрактно написать метод добавления, удаления, изменения и проверки базы данных в модель и выставляем наши данные через параметры, где, поля условия. Обычно это может значительно повысить эффективность и повторное использование кода. Например вот так:

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

Наконец

Если что-то не так, поправьте меня, THX~