代碼簡潔之三:減少注釋 增加代碼可讀性


最近一直在研讀《代碼簡潔之道》這本書,里面很多非常細致的對代碼重構和簡化的好的思想和方法值得學習,我做了一些筆記,在之前的一些博客中也有涉及。

關於注釋,各家有各家的想法。有人認為注釋越詳細越好,從目的到參數含義,無微不至的去寫注解,比如api接口之類的。也有人認為,涉及復雜的業務邏輯,需要寫必要注釋(之前我也是這么認為的)。也有人認為注釋是罪惡根源,不該寫任何注釋,應該讓清晰易懂的代碼自己說話。

我想起了很久之前看涵哥寫的api項目,里面幾乎沒有什么冗余的注釋,代碼簡潔到了極致,每一步都像在讀一句詩。而且大量使用componets方式,用大量組合對象去單獨處理,減少耦合。當時我問過他為何看不到什么注釋,他答:代碼是最好的注釋。

以前不是非常認同,還是認為注釋不能缺少,特別是業務非常復雜,注釋甚至能幫助理清邏輯業務,也作為思路的一種記錄。

博客園上已經出現了一篇不錯的文章,講不要濫用注釋:

http://www.cnblogs.com/leotsai/p/anti-code-comment.html

而《代碼簡潔之道》講的更透徹,妙語連珠,下面是我記錄的一些筆記:

注釋的唯一恰當的用處在於彌補我們用代碼解析意圖所遭遇的失敗。

注釋存在時間越久,它和真實代碼之間的關系就越來越淡,最終甚至全錯。

應該把主要精力用在寫清晰的代碼上,直接保證無須編寫注釋。

注釋不能美化糟糕的代碼。

注釋協助如何改寫代碼,讓注釋消失,讓代碼更readable.

盡量利用函數名來傳遞信息。


甚至todo注釋也成了罪惡的替罪羊,也不是容許我們寫糟糕代碼的借口。

每個函數都需要注釋太迂腐,盡力還是判斷。杜絕那種看起來像廢話一樣的注釋,如果代碼足夠清晰就不要寫多於的注釋。

用好的代碼替換廢話注釋,讓自己也變得更加輕松和優秀。

歸屬和署名都應該交給版本控制而不是寫在注釋里面。除非你一直不去動那些代碼,否則你無法保證這段代碼到底是誰該負責

被注釋掉的代碼應刪了,留着給人迷惑,還認為可能某天還會用到。

如果一定要加注釋,請讓它離它解釋的代碼足夠近

注釋要足夠容易懂,如果注釋還需要解釋,那么就太失敗了。

 

我不喜歡數學上那種死死的公理定理,但是這些思想是可以指導我們更好的改善代碼,讓后面的工作變得輕松。

 

唯一靠譜能提高編程質量和速度的方法就是:時刻保持代碼簡潔

 

我以此思想和方法重新review了我去年11月寫的一個采集小說的腳本,源碼寫得很low:

<?php
/**
 * @author freephp
 * @date 2015-11-13
 *
 */
class MyCurl {    
    private static  $url = '';
    private static $oriUrl = ''; // referer url
    private static $data = array(); // ���ܷ��������� post,put
    private   static $method; // ���ʷ�ʽ��Ĭ����GET����
    
    public static function send($url, $data = array(), $method = 'get') {
        if (!$url) exit('url can not be null');
        self::$url = $url;
        self::$method = $method;
        $urlArr = parse_url($url);
        self::$oriUrl = $urlArr['scheme'] .'://'. $urlArr['host'];
        self::$data = $data;
        if ( !in_array(
                self::$method,
                array('get', 'post', 'put', 'delete')
             )
           ) {
                    exit('error request method type!');
             }
        
                $func = self::$method . 'Request';
                return self::$func(self::$url);
    }
    /**
     * ��������curl������
     * @param int $is_post �Ƿ���post����
     */
    private  function doRequest($is_post = 0) {
        $ch = curl_init();//��ʼ��curl
        curl_setopt($ch, CURLOPT_URL, self::$url);//ץȡָ����ҳ
        curl_setopt($ch, CURLOPT_AUTOREFERER, true);
        // ��Դһ��Ҫ���ó����Ա�վ
        curl_setopt($ch, CURLOPT_REFERER, self::$oriUrl);
        /**
         * special settings about headers
         * 
         */
        $headers = array(
                'Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
                'Accept-Encoding:gzip, deflate, sdch',
                'Accept-Language:zh-CN,zh;q=0.8',
                'Cache-Control:max-age=0',
                'Connection:keep-alive'
        );
        curl_setopt($ch, CURLOPT_HEADER, $headers);
        
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);//Ҫ����Ϊ�ַ������������Ļ��
        if($is_post == 1) curl_setopt($ch, CURLOPT_POST, $is_post);//post�ύ��ʽ
        if (!empty(self::$data)) {
            self::$data = self::dealPostData(self::$data);
            curl_setopt($ch, CURLOPT_POSTFIELDS, self::$data);
        }
        
        $data = curl_exec($ch);//����curl    
        curl_close($ch);
        return $data;
    }
    /**
     * ����get����
     */
    public function getRequest() {
        return self::doRequest(0);
    }
    /**
     * ����post����
     */
    public function postRequest() {
        return self::doRequest(1);
    }
    /**
     * �������get����Ĵ�������
     * 
     * @param array $postData
     */
    public function dealPostData($postData) {
        $postStr = '';
        if (!is_array($postData)) exit('post data should be array');
        foreach ($postData as $k => $v) {
            $postStr .= "$k=" . urlencode($v) . "&";
        }
        return substr($postStr, 0, -1);
    }
    /**
     * ����put����
     */
    public function putRequest($param) {
        return self::doRequest(2);
    }
    
    /**
     * ����delete����
     */
    public function deleteRequest($param) {
        return self::doRequest(3);
    }
    
    
}

function url_exists($url)
{
    $head = @get_headers($url);
    
    return ($head[0] == 'HTTP/1.1 404 Not Found') ?  false : true;
    
}
/* $curl = new MyCurl('http://www.jumei.com',array(), 'get');
$res = $curl->send(); */
/* $res = MyCurl::send('http://www.ipip.net/ip.html',array('ip' => '61.142.206.145'),'post'); */
$pageNum = 941672;
//$pageNum = 1323233;
set_time_limit(0);
require 'Analyzer.php';
$start = microtime(true);

$contents = file_get_contents('E:\nodejs\chapterUrls.txt');
$urls = array_unique(explode("\r\n", $contents));

/**
 * ׼�����
 */
/*$conn = mysqli_connect('127.0.0.1', 'root', '12345','novel');*/
$analyzer =new Analyzer();
foreach ($urls as $url) {
    $res = MyCurl::send($url,array(),'get');

    $title = $analyzer->getTitle($res)[1];
    //var_dump($title);die();
    
    $content = $analyzer->getContent('div', 'content', $res)[0];
    
    
    // &nbsp;&nbsp;&nbsp;&nbsp;
    
    
    $allContents = $title . "<br/>". $content;
    $filePath = './juewangjiaoshi/' . $title . '.html';
    if(!file_exists($filePath)) {
        $analyzer->storeToFile($filePath, $allContents);
    } else {
        continue;
    }
    echo 'down the url:' , $url , "\r\n";
   /* $sql = "insert into desks (title,content) values('" . $title . "','" . "$content')";
    $fn = fopen('sql.txt', 'a+');
    fwrite($fn, $sql . "\r\n");
    fclose($fn);
    echo $sql;die();
    mysqli_query($conn, $sql);*/
}

$end = microtime(true);
$cost = $end - $start;
echo "total cost time:" . round($cost, 3) . " seconds\r\n";
View Code

 

由於我不小心,把文件的中文注釋搞成亂碼了,又沒法恢復,大家將就着看。里面充斥着命名不規范的變量和多余的廢話似的注釋,還有一些因為為了考慮未來需求而冗余的代碼(如putRequest,delRequst),還有一些調試信息被注釋,而沒被刪除。

我的修改之后的代碼如下:

<?php
/**
 * Class MyCurl
 *
 * @date review at 2016-01-07
 * @author freephp<fightforphp@gmail.com>
 */
class MyCurl {    
    private static  $url = ''; 
    private static $refererUrl = '';
    private static $data = array(); // the data from post,put method
    private   static $method; // request method

    public static function send($url, $method = 'get', $data = array())
    {
        if (!$url) exit('url can not be null');
        list (self::$url, self::$method, self::$data) = [$url, $method, $data];
        self::$refererUrl = self::_getPureUrl();

        if (!self::_isValidMethod()) {
            exit('error request method type!');
        }

        return self::doRequest();
    }

    private static function _getPureUrl() {
        $urlArr = parse_url(self::$url);
        return $urlArr['scheme'] .'://'. $urlArr['host'];
    }

    private static function _isValidMethod() {
        return (in_array(self::$method, array('get', 'post', 'put', 'delete')))? true : false;
    }

    /**
     * make curl request
     */
    private  function doRequest() {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, self::$url);// set the url
        curl_setopt($ch, CURLOPT_AUTOREFERER, true);
        // set the referer url,some website will check this param
        curl_setopt($ch, CURLOPT_REFERER, self::$refererUrl);
        /**
         * special settings about headers
         * 
         */
        $headers = array(
                'Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
                'Accept-Encoding:gzip, deflate, sdch',
                'Accept-Language:zh-CN,zh;q=0.8',
                'Cache-Control:max-age=0',
                'Connection:keep-alive'
        );
        curl_setopt($ch, CURLOPT_HEADER, $headers);
        
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);// set the curl return the transfer
        if(self::$method == 'post') curl_setopt($ch, CURLOPT_POST, 1);//if use post method,set the flag
        if (!empty(self::$data)) {
            self::$data = self::buildPostStr(self::$data);
            curl_setopt($ch, CURLOPT_POSTFIELDS, self::$data);
        }
        
        $data = curl_exec($ch);
        curl_close($ch);

        return $data;
    }

    /**
     * make the post data to string.
     * 
     * @param array $postData
     * @return string
     */
    public function buildPostStr($postData) {
        $postStr = '';
        if (!is_array($postData)) exit('post data should be array');
        foreach ($postData as $k => $v) {
            $postStr .= "$k=" . urlencode($v) . "&";
        }
        return substr($postStr, 0, -1);
    }
}

/**
 * ׼client to run
 */
set_time_limit(0);
require 'Analyzer.php';
$start = microtime(true);
$urls = file_get_contents('E:\nodejs\chapterUrls.txt');
$urls = array_unique(explode("\r\n", $urls));

$analyzer =new Analyzer();
foreach ($urls as $url) {
    $res = MyCurl::send($url, 'get');

    $title = $analyzer->getTitle($res)[1];

    $content = $analyzer->getContent('div', 'content', $res)[0];
    
    $allContents = $title . "<br/>". $content;
    $filePath = './juewangjiaoshi/' . $title . '.html';
    if(!file_exists($filePath)) {
        $analyzer->storeToFile($filePath, $allContents);
    } else {
        continue;
    }
    echo 'down the url:' , $url , "\r\n";
}

$end = microtime(true);
$cost = $end - $start;
echo "total cost time:" . round($cost, 3) . " seconds\r\n";

 

從之前的160+行縮減到116行。

1.把一些變量命名修改成更易讀的,比如$oriUrl不明所以,改為$refererUrl.

2.將一些組裝和處理參數的過程重新extract method,將putRequest之類的刪去,把整個類變得更干凈。

3.把在foreach中的new Analyzer()移出去,防止重復去new。

4.刪除調試和被注釋掉的代碼,讓一切不使用的代碼都灰飛煙滅吧!

我依然保留了一些注釋,我認為足夠精准,且有引導作用。我不完全贊同0注釋,我更傾向於極簡的代碼+必要補充性注釋。

 

關於減少注釋或者說完全消滅注釋,要根據情況和項目而定。如果是個人項目,建議可以盡量減少不必要注釋,用代碼可讀性去代替注釋作用。但是由於很多項目是多人合作,每個人對英文的掌握能力和理解能力等編程相關能力各不同,一味追求0注釋,可能造成代碼和業務更難被熟悉和理解。在一些復雜業務中,良好的注釋不可或缺,比如繁瑣的結算,比如驗證規則多樣,都需要有對業務的分析和總結的注釋來引導理解代碼邏輯。

 

總之,減少為了彌補代碼寫得不清不楚或抽象層分層不清晰的注釋,保留和優化補充和提示性的注釋,同時持續保持代碼最簡。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM