如何成為一名優(yōu)秀的工程師(語義篇)
好的語義表達(dá)是團(tuán)隊(duì)協(xié)作中高效迭代的潤滑劑,好的語義表達(dá)是線上未知代碼問題排查的指南針。
本篇文章巨長,如果你比較“懶”,來我講給你聽(直播中有更多細(xì)節(jié)) 回放地址
看完這個(gè)還不過癮?學(xué)習(xí)使你快樂?還想學(xué)習(xí)?快上車
不要讓其他人讀不懂你的代碼,其他人可能就是一周后的你。時(shí)刻以“如果你寫的這段代碼出現(xiàn)故障,一個(gè)陌生人接手你的代碼需要多久能處理完這個(gè)bug”來監(jiān)督自己。
日常中應(yīng)該多多刻意提升自己語義表達(dá),百利而無一害。那么我們應(yīng)該從哪些細(xì)節(jié)去做好語義表達(dá)呢?
以下代碼全為我的藝術(shù)創(chuàng)作,不屬于任何實(shí)際項(xiàng)目
命名
案例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;
 - }
 - }
 
案例2
- function getUserInfo($teamId, $youId = [])
 - {
 - }
 
如果僅僅有這個(gè)函數(shù)名和參數(shù)名,誰能猜到參數(shù)的意義呢?
案例3
- class Db
 - {
 - /**
 - * @param string $table 數(shù)據(jù)庫表名
 - * @param array $data 新增數(shù)據(jù)
 - *
 - * @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);
 - }
 - }
 
案例4
假如業(yè)務(wù)代碼里有這些類
- class WechatUserModel{
 - }
 - class WechatGroupModel{
 - }
 - class WechatMessageModel{
 - }
 
而我們查詢數(shù)據(jù)庫發(fā)現(xiàn)
這樣我們根據(jù)業(yè)務(wù)代碼就非常不方便找到對(duì)應(yīng)的表,而且其他人接手我們項(xiàng)目的時(shí)候,也會(huì)摸不著頭腦?;蛘哒f這可能是三個(gè)人三次迭代開發(fā)造成的,那么他們彼此都沒有去參考前面人的命名規(guī)則。
來自靈魂的拷問
注釋
說完命名,下面說下注釋。注釋里還有什么學(xué)問?Are you kidding me?
一個(gè)數(shù)組對(duì)象成員,你知道怎么寫嗎?
類的魔術(shù)方法調(diào)用的注釋,你知道怎么寫嗎?
對(duì)象數(shù)組
- /**
 - * @var Ads[]
 - */
 - public $adsList = [];
 
- $blocks = [];/** @var $blocks Block[] **/
 
@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;
 - }
 - }
 
@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"],
 - ];
 - }
 - }
 
注釋其他注意事項(xiàng)
注釋解釋張冠李戴,方法名更新,方法的功能業(yè)務(wù)注釋沒更新;復(fù)制別人的代碼把 @author 信息也復(fù)制過來了,錯(cuò)誤了還要把鍋甩給別人。
注釋更多參考 http://manual.phpdoc.org/HTML...
函數(shù)、方法
案例1
先說明一句,不好的代碼不妨礙它成為一個(gè)優(yōu)秀的軟件。PHP MySQL 爛代碼多的去了。
找到一個(gè)開源軟件里面的代碼,功能非常搶到,但是這個(gè)方法內(nèi)容太多,一些不足點(diǎn)我標(biāo)注出來了。
案例2
拿上面我舉例子,還記得下面這種圖嗎?
優(yōu)化方案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);
 - }
 - }
 
優(yōu)化方案2
- class Db
 - {
 - /**
 - * @param string $table 數(shù)據(jù)庫表名
 - * @param Entity $data 新增對(duì)象
 - *
 - * @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
 - {
 - /**
 - * 針對(duì)成員都是私有屬性的對(duì)象
 - *
 - * @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();
 - // 如果不是駝峰命名方式,就把對(duì)象里面的 createTime 轉(zhuǎn)成 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);
 - }
 - }
 - // 測(cè)試
 - $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
這還是函數(shù)嗎?(不僅僅是語義,屬于錯(cuò)誤)
- /**
 - * @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兩個(gè)參數(shù)重復(fù)
案例4
盡量減少參數(shù)引用
- function bad($input1, $input2, &$input3)
 - {
 - //...logic
 - $input3 = "xxx";
 - return true;
 - }
 
案例5
參數(shù)類型明確,返回值類型明確,不要出現(xiàn) mixed。這個(gè)我直接拿官方的函數(shù)來舉例,對(duì)權(quán)威也要有懷疑的眼光。純屬個(gè)人看法。
案例6
上面例子中你會(huì)發(fā)現(xiàn)這個(gè)addUser寫得不想一個(gè)函數(shù)(方法)而像一個(gè)遠(yuǎn)程api接口。而且在右邊的代碼中需要每次使用的時(shí)候都要用is_array來判斷。這是非常不友好的語義表達(dá)。PHP Java 這樣的高級(jí)語言有異常,我們要善用異常。
好的語義表達(dá)是團(tuán)隊(duì)協(xié)作中高效迭代的潤滑劑,好的語義表達(dá)是線上未知代碼問題排查的指南針。這篇博客到這里就結(jié)束了,不知道你是否有一些收獲呢?































 
 
 

 
 
 
 