如何成爲一名優秀的工程師(語義篇)

好的語義表達是團隊協做中高效迭代的潤滑劑,好的語義表達是線上未知代碼問題排查的指南針。

本篇文章巨長,若是你比較「懶」,來我講給你聽(直播中有更多細節) 回放地址

看完這個還不過癮?學習使你快樂?還想學習?快上車

不要讓其餘人讀不懂你的代碼,其餘人可能就是一週後的你。時刻以「若是你寫的這段代碼出現故障,一個陌生人接手你的代碼須要多久能處理完這個bug」來監督本身。php

平常中應該多多刻意提高本身語義表達,百利而無一害。那麼咱們應該從哪些細節去作好語義表達呢?sql

image.png

如下代碼全爲個人藝術創做,不屬於任何實際項目

命名

案例1

function getGoods($query, $shopId)
{
    $goodsId = Goods::add($query["uid"], $query["name"]);
    return Shop::add($goodsId, $shopId);
}

class Goods
{
    public static function add($uid, $name)
    {
        $id = mt_rand(1, 100000);
        return $id;
    }
}

class Shop
{
    public static function add($goodsId, $shopId)
    {
        $id = mt_rand(1, 100000);
        return $id;
    }
}

image.png

案例2

function getUserInfo($teamId, $youId = [])
{

}

若是僅僅有這個函數名和參數名,誰能猜到參數的意義呢?
image.png數據庫

案例3

class Db
{
    /**
     * @param string $table 數據庫表名
     * @param array  $data  新增數據
     *
     * @return int 新增主鍵
     */
    public static function insert(string $table, array $data)
    {
        $id = mt_rand(1, 1000);
        return $id;
    }
}

class ViewLogStore
{
    private $table = "view_log";

    function setHistory($data)
    {
        Db::insert($this->table, $data);
    }
}

image.png

案例4

假如業務代碼裏有這些類segmentfault

class WechatUserModel{

}

class WechatGroupModel{

}

class WechatMessageModel{

}

而咱們查詢數據庫發現
image.pngapi

這樣咱們根據業務代碼就很是不方便找到對應的表,並且其餘人接手咱們項目的時候,也會摸不着頭腦。或者說這多是三我的三次迭代開發形成的,那麼他們彼此都沒有去參考前面人的命名規則。數組

來自靈魂的拷問函數

image.png

註釋

說完命名,下面說下注釋。註釋裏還有什麼學問?Are you kidding me?
一個數組對象成員,你知道怎麼寫嗎?
類的魔術方法調用的註釋,你知道怎麼寫嗎?學習

對象數組

/**
 * @var Ads[]
 */
public $adsList = [];

image.png

$blocks = [];/** @var $blocks Block[] **/

image.png

@method 的使用

/**
 * @link http://manual.phpdoc.org/HTMLframesConverter/default/
 *
 * @method static int search(string $query, $limit = 10, $offset = 0)
 */
class SearchServiceProxy
{
    public static function __callStatic($method, $arguments)
    {
        if (!method_exists("SearchService", $method)) {
            throw new \LogicException(__CLASS__ . "::" . $method . " not found");
        }

        try {
            $data = call_user_func_array(["SearchService", $method], $arguments);
        } catch (\Exception $e) {
            error_log($e->getMessage());
            return false;
        }

        return $data;
    }
}

image.png

@deprecated 使用

class SearchService
{

    /**
     * @param string $query
     * @param int    $limit
     * @param int    $offset
     *
     * @return array
     * @deprecated
     */
    public static function search(string $query, $limit = 10, $offset = 0)
    {
        return [
            ["id" => 1, "aaa"],
            ["id" => 2, "bbb"],
        ];
    }
}

image.png

註釋其餘注意事項

註釋解釋張冠李戴,方法名更新,方法的功能業務註釋沒更新;複製別人的代碼把 @author 信息也複製過來了,錯誤了還要把鍋甩給別人。測試

註釋更多參考 http://manual.phpdoc.org/HTML...

函數、方法

案例1

先說明一句,很差的代碼不妨礙它成爲一個優秀的軟件。PHP MySQL 爛代碼多的去了。
找到一個開源軟件裏面的代碼,功能很是搶到,可是這個方法內容太多,一些不足點我標註出來了。
image.pngfetch

案例2

拿上面我舉例子,還記得下面這種圖嗎?
image.png

優化方案1

class ArrayUtils{
    public static function fetch($arr, $keys, $setNull = false)
    {
        $ret = array();
        foreach($keys as $key)
        {
            if ($setNull)
            {
                $ret[$key] = $arr[$key];
            }
            else
            {
                isset($arr[$key]) && $ret[$key] = $arr[$key];
            }
        }
        return $ret;
    }
}


class ViewLogStore
{
    private $table = "view_log";

    function record($data)
    {
        $fields = array(
            'uid',
            'url',
            'referer',
            'created_time'
        );
        $data = ArrayUtils::fetch($data, $fields);
        Db::insert($this->table, $data);
    }
}

優化方案2

class Db
{
    /**
     * @param string $table 數據庫表名
     * @param Entity $data  新增對象
     *
     * @return int 新增主鍵
     */
    public static function insert(string $table, Entity $data)
    {
        $array = $data->toArray();
        var_export($array); // test

        $id = mt_rand(1, 1000);
        return $id;
    }
}

class ArrayUtils
{
    /**
     * 針對成員都是私有屬性的對象
     *
     * @param      $obj
     * @param bool $removeNull 去掉空值
     * @param bool $camelCase
     *
     * @return array
     */
    public static function Obj2Array($obj, $removeNull = true, $camelCase = true)
    {
        $reflect = new \ReflectionClass($obj);
        $props = $reflect->getProperties(\ReflectionProperty::IS_PUBLIC | \ReflectionProperty::IS_PRIVATE | \ReflectionProperty::IS_PROTECTED);

        $array = [];
        foreach ($props as $prop) {
            $prop->setAccessible(true);
            $key = $prop->getName();

            // 若是不是駝峯命名方式,就把對象裏面的 createTime 轉成 create_time
            if (!$camelCase) {
                $key = preg_replace_callback("/[A-Z]/", function ($matches) {
                    return "_" . strtolower($matches[0]);
                }, $key);
                $key = ltrim($key, "_");
            }

            $value = $prop->getValue($obj);

            if ($removeNull == true && $value === null) {
                continue;
            }

            if (is_object($value)) {
                $value = self::Obj2Array($value);
            }

            $array[$key] = $value;
        }

        return $array;
    }
}

class Entity
{
    public function toArray(){
        return ArrayUtils::Obj2Array($this);
    }
}

class ViewLogEntity extends Entity
{
    /**
     * @var int
     */
    private $uid;

    /**
     * @var string
     */
    private $url;

    /**
     * @var string
     */
    private $referer;

    /**
     * @var string
     */
    private $createdTime;

    /**
     * @param int $uid
     */
    public function setUid(int $uid)
    {
        $this->uid = $uid;
    }

    /**
     * @param string $url
     */
    public function setUrl(string $url)
    {
        $this->url = $url;
    }

    /**
     * @param string $referer
     */
    public function setReferer(string $referer)
    {
        $this->referer = $referer;
    }

    /**
     * @param string $createdTime
     */
    public function setCreatedTime(string $createdTime)
    {
        $this->createdTime = $createdTime;
    }
}


class ViewLogStore
{
    private $table = "view_log";

    function record(ViewLogEntity $viewLogEntity)
    {
        Db::insert($this->table, $viewLogEntity);
    }
}

// 測試

$viewLogEntity = new ViewLogEntity();
$viewLogEntity->setUid(1);
$viewLogEntity->setReferer("https://mengkang.net");
$viewLogEntity->setUrl("https://segmentfault.com/l/1500000018225727");
$viewLogEntity->setCreatedTime(date("Y-m-d H:i:s",time()));

$viewLogStore = new ViewLogStore();
$viewLogStore->record($viewLogEntity);

案例3

這仍是函數嗎?(不單單是語義,屬於錯誤)

/**
 * @method mixed fetchList(string $sql, array $argv);
 */
class Model
{

    public function __construct($table)
    {

    }
}

function getUserList($startId, $lastId, $limit = 100)
{
    if ($lastId > 0) {
        $startId = $lastId;
    }

    $sql = "select * from `user` where id > ? order by id asc limit ?,?";

    $model = new Model('user');
    return $model->fetchList($sql, [intval($startId), intval($limit)]);
}

$startId$lastId兩個參數重複

案例4

儘可能減小參數引用

function bad($input1, $input2, &$input3)
{
    //...logic

    $input3 = "xxx";

    return true;
}

案例5

參數類型明確,返回值類型明確,不要出現 mixed。這個我直接拿官方的函數來舉例,對權威也要有懷疑的眼光。純屬我的見解。
image.png

案例6

image.png

上面例子中你會發現這個addUser寫得不想一個函數(方法)而像一個遠程api接口。並且在右邊的代碼中須要每次使用的時候都要用is_array來判斷。這是很是不友好的語義表達。PHP Java 這樣的高級語言有異常,咱們要善用異常。

image.png

好的語義表達是團隊協做中高效迭代的潤滑劑,好的語義表達是線上未知代碼問題排查的指南針。這篇博客到這裏就結束了,不知道你是否有一些收穫呢?
image.png

放三個二維碼
image.png

累死啦,原創博客不容易,若是以爲不錯,能夠打賞下哈。謝謝。

相關文章
相關標籤/搜索