我如今是這樣編程的

注:文中圖片均來自網絡程序員

 

我在作什麼

曾經,我試過接到一些需求。一眼帶事後,腦殼立刻隨着高昂的鬥志沉溺在代碼的世界中 ,快馬加鞭地敲着鍵盤直到最後測試的完成。我從思緒中恢復過來,乍一看本身寫的功能,和需求差了十萬八千里,我TM都在幹嗎?數據庫

除此以外,我還見過相似的很可笑的事情。有一個程序員,經理提了需求,而後他在那裏折騰了一天。結果不但沒作出來,並且和實際需求都是徹底搭不上調。通過詢問發現,他不知道經理說了什麼,也不知道本身到底在作什麼。編程

代碼的世界多是昏天暗地的,可是咱們的思惟不能這樣隨之混亂,不然一切都會前功盡棄。因此我如今編寫程序的時候,常常會想一下:我要作什麼,我在作什麼。更好的方法是把詳細需求落實到文檔,並時刻覈對文檔。設計模式

 

 

大局爲重

2-8法則告訴咱們,一個項目核心的功能只有不多,其它大部分都是對核心功能輔助或加強的。但當任務分發下來,我手頭總有一些本身很想開發的模塊,不過它們不屬於那20%。我之前常常會在這些感興趣的模塊上花費不少時間和精力。緩存

結果項目快要到上線期限,主要的功能卻沒開發完成,其它一些不起眼的功能卻作得很好,但爲此項目不得不延期了。若是反過來,只要對總體功能預期不會有太大誤差,能夠將就的先上線。重要一點是:即便功能還有遺漏,但項目能夠上線了,老闆天然不會太追究,本身工做也能圖個安心。若是不知道那些功能模塊是最重要的,先問問經理。網絡

人老是喜歡作一些本身感興趣或者有挑戰的事。不過在這方面,爲了項目和團隊着想,應該儘可能壓制這種誘惑。編程語言

 

 

性能永遠不是優先考慮的問題

我歷來不會一開始就考慮性能問題。若是項目成本很低,甚至到項目結束時,若是沒有感受到明顯的性能問題,也不會去管。要知道如今已經不是DOS的年代,CPU的計算能力很高,但成本很低了。重要一點是,若是隻針對提高性能對代碼作改動,很容易破壞代碼的複用性和可維護性。而返過來,提升了代碼的複用性和可維護性,則很容易提升性能。函數

 

下面有一個PHP的代碼實例,功能是幫助用戶重置密碼(代碼爲了簡單說明問題,請不要太在乎一些無關的細節)工具

requestResetPassword是接收用戶重置密碼的請求而且作了相應的檢查。爲了更好的複用性,我將重置密碼的操做單獨分配到一個新的resetPassword的函數,更改完密碼的後再調用sendEmail向用戶發送一封通知郵件。性能

/**
 * 用戶請求重置密碼的接收器
 */
function requestResetPassword() {
    //檢查用戶是否存在
    if( !checkUserExists( $_GET['userid'] ) ) {
        exit('抱歉,用戶不存在,請確認用戶賬號。');
    }
    resetPassword( $_GET['userid'] );
    //最後向用戶發送一封郵件
    sendEmail( $_GET['userid'], '重置密碼成功', '新的密碼是xxxx' );
    exit('新密碼已經發送到你的郵箱。');
}


/**
 * 幫助用戶重置密碼
 */
function resetPassword( $userid ) {
    //檢查用戶是否存在
    if( !checkUserExists( $userid ) ) {
        return false;
    }

    //進行重置用戶密碼的操做
    //略...
    return true;
}


/**
 * 向用戶發送一封郵件
 */
function sendEmail( $userid, $title, $content ) {
    //檢查用戶是否存在
    if( !checkUserExists( $userid ) ) {
        return false;
    }

    //發送郵件操做
    //略...
    return true;
}


/**
 * 檢查某個用戶是否存在
 */
function checkUserExists( $userid ) {
    $user = getUserInfo( $userid );
    return !empty( $user );
}


/**
 * 獲取某個用戶的數據
 */
function getUserInfo( $userid ) {
    //假設我有一個query的函數,它用來查詢數據庫並返回數據
    $user = query( "SELECT * FROM `user` WHERE `uid`=" . intval( $userid ) );
    return is_array( $user ) ? $user : array() ;
}

  

如今問題是,這三個函數都同時使用checkUserExists這個函數來檢查用戶不存在,數據庫查詢了三次,這樣帶來了一些額外的開銷。

若是要去掉三者之間任意一個checkUserExists,看上去是可能的。可是若是以後有某些功能要調用resetPassword或者sendEmail,用戶不存在時,系統可能會發生錯誤。

還有一個解決方法是,將resetPassword的邏輯寫到requestResetPassword裏,再過一點,把sendEmail的邏輯也寫進去。這樣函數調用減小,數據庫查詢也變成一次了,性能獲得了提升。可是重置密碼和發送郵件的功能將不能獲得複用,而且違背了單一責任的原則,代碼複雜度也提升了。

 

不過,由於函數分離和複用性都很好,若是實際性能受到影響,可能考慮用緩存的方法減小數據庫查詢,我改動了它們共用的checkUserExists函數:

/**
 * 檢查某個用戶是否存在
 */
function checkUserExists( $userid ) {
    //增長一個緩存,用以記錄檢查用戶的結果
    static $cache = array();

    //檢查當前用戶是否已經檢查過一次
    if( isset( $cache[ $userid ] ) ) {
        return $cache[ $userid ];
    }

    $user = getUserInfo( $userid );
    //把結果記錄到緩存中
    $cache[ $userid ] = !empty( $user );

    return $cache[ $userid ];
}

 

也能夠用一樣的方法改動getUserInfo函數。

 

這裏能夠看到,當代碼的複用性提升時,想提升性能是很簡單的,性能的瓶頸也很容易被發現和修改。

儘管這個例子對性能影響還不夠大,還有一些影響更大的,好比說遍歷,我可能爲了複用而將遍歷封裝到一個函數中,而且屢次使用它。這些開銷對個人項目根本沒有預想中那樣有太大的影響,或者說是微乎其微的。因此我更願意把時間花在如何提升代碼的複用性和維護性方面,而不是糾結於浪費多這一點性能。實際性能若是真的達不到要求,也能夠權衡增長硬件配置。

 

 

名字長一點好

函數名和變量名等除了給機器看,也要給人看的,有時一個簡單直接的好名字實在是很難想,這時不妨用長一點的名字更好。可讀性更好:

//好名字
class ErasedTypeEquivalence {
}

//壞名字
class ErdTypeEqe {
}

//好名字
function checkUserExists () {
}

//壞名字
function ckUserExt() {
}

//好名字
$result;

//壞名字
$ret;

 

我見過一些代碼,因爲簡單寫過多,整遍代碼不少都是4個字母或如下的,可讀性很是差,固然不排除是爲了偷懶。

但若是想有更多的時間騰出來偷懶,不該該在這上面玩小聰明,不然這時我如今應該在思考前幾天的代碼是在寫什麼。

 

什麼?短名字會讓代碼執行得更快? 那證實給我看,若是真的快,快了多少?

 

 

自說明代碼很重要,但註釋一樣重要

代碼自己能夠說明問題的確是很棒的,但並非說註釋不重要,有時候我更喜歡先看註釋,由於它總比我看代碼更快的瞭解這程序是作什麼的。

若是我把本文前面說性能的例子去掉註釋,哪一個能讓你更快了解代碼的意圖?或者說,你更願意看哪一個?

function requestResetPassword() {
    if( !checkUserExists( $_GET['userid'] ) ) {
        exit('抱歉,用戶不存在,請確認用戶賬號。');
    }
    resetPassword( $_GET['userid'] );
    sendEmail( $_GET['userid'], '重置密碼成功', '新的密碼是xxxx' );
    exit('新密碼已經發送到你的郵箱。');
}


function resetPassword( $userid ) {
    if( !checkUserExists( $userid ) ) {
        return false;
    }

    //進行重置用戶密碼的操做
    //略...
    return true;
}


function sendEmail( $userid, $title, $content ) {
    if( !checkUserExists( $userid ) ) {
        return false;
    }

    //發送郵件操做
    //略...
    return true;
}


function checkUserExists( $userid ) {
    static $cache = array();

    if( isset( $cache[ $userid ] ) ) {
        return $cache[ $userid ];
    }

    $user = getUserInfo( $userid );
    $cache[ $userid ] = !empty( $user );

    return $cache[ $userid ];
}


function getUserInfo( $userid ) {
    $user = query( "SELECT * FROM `user` WHERE `uid`=" . intval( $userid ) );
    return is_array( $user ) ? $user : array() ;
}

 

因此,即便代碼自己很清晰,可是加上註釋的話,可讀性也能提升不少!

 

 

適當抽象

編程就是爲了解決實際中的問題,在思考如何編碼的時候,把問題抽象到必定的高度去思考,更容易把握問題所在。不過更多時候,我發現從代碼抽象到現實的例子是有必定難度的,同時我也相信,編程高手也是抽象高手,他們很容易把問題反映到真實生活中去。

不過若是常常留意和思考生活中的細節,會提高本身的抽象能力。

舉一個螺絲刀的例子,若是叫你造一個螺絲刀,你會作成什麼樣子?我這裏有三把不一樣的螺絲刀:

 

顯然第一種螺絲刀是最簡單的,比較中規中矩。

第二種螺絲刀中間能夠旋轉刀柄,讓刀柄和刀頭成90度,這樣的設計讓擰螺絲更加輕鬆。

第三種螺絲刀則能夠更換刀頭,若是之後有其它類型的螺絲,則只要造一個適合這種螺絲的刀頭就能夠了。

 

那反映到編程中的問題,若是項目要增長一個工具類庫。

第一種方法,能夠直接把類庫的所需功能寫出來就能夠了。

第二種方法,不但把類庫寫出來,並且針對項目的一些狀況作特殊改進,使得在這個項目中更好用。

第三種方法,根據類庫的特性,把公共部分的邏輯作成接口,特殊的部分分離出來單獨實現,若是之後要增長相同類型的類庫,則實現特殊部分的邏輯,而後接入接口便可。

 

可是在抽象的時候,要避免不合理的抽象,有時也可能形成過渡設計,如今只須要一種螺絲刀,但你卻把更多類型的螺絲刀都作出來了(並且仍是瑞士軍刀的樣子。。):

 

 

一致性

團隊開發中,可能每一個人的編程風格都不同,拿花括號來講,有些人喜歡和代碼在同一行,而有些喜歡獨自一行

//例一
function func() {
}

//例二
function func()
{
}

 

命名風格也都不同,好比說聲明變量接收一個函數返回的數據,有些喜歡用result,有些喜歡用data。

它們可能都很好,不過在團隊開發中,儘可能統一用同一種風格可以很好的減小交叉開發的成本。

 

將錯就錯

面對項目一些可有可無的分歧或錯誤,應該要接受和理解。承接上面的問題,若是團隊中已經有人大量用了data的變量命名,但你認爲result的更符合當前情況的描述。這種狀況,我優先選擇data命名,由於若是再使用result的話,會破壞項目的一致性,對開發沒有任何好處。

這只是不多的一方面,若是項目規範沒有很好的落實,實際工做中會有大量的一致性問題,必須靠團隊每一個人的決心和責任心去把它作好。一般,加入一個正在開發中的項目,編寫功能前,我都會首先看項目以前的相似的代碼,並儘可能模仿他們的寫法。不過,若是有明顯的錯誤,應該及時指出和修正。

只要堅持把一致性作好,不少方法會成爲團隊甚至業界的標準,即便它們不是最好的,可是有什麼關係呢?

 

 

適當休息

編程的時候若是沒有思路或者感到混亂,到外邊休息10分鐘,或者看一下風景,讓腦殼清醒一下是很好的。這招很管用,親測。

 

 

至少把代碼完整運行一次

有時函數的邏輯過於簡單,以致於會認爲這個不可能發生錯誤,但事實上最容易發生錯誤的一般就是這些代碼,常見的單詞拼寫錯誤,參數錯誤,還有一些意料以外的問題。因此不管什麼狀況,我都會把代碼完整運行一遍。

固然更好的作法是用一些系統的測試方法,好比說單元測試。

 

 

編程不是藝術 

從一開始,編程語言的出現和發展,都是爲了解決現實生活中的問題,包括它自身產生的問題。

面向對象、設計模式的出現,是用來解決編程語言自身帶來的可讀性和維護性等問題,而不是爲了讓編程語言上升到藝術的層面。儘管編程中有‘優雅’一詞,但我更認爲它只是用來形容代碼更容易讓人讀懂和維護。

我拒絕一切看起來很‘優雅’,卻不能爲編程工做帶來一點好處的代碼。若是你喜歡玩弄語言,應該去看成家。 

 

 

甘於平凡

程序員真的很高傲,在我接觸過的人中,包括我本身也是。我之前常常對一些簡單的代碼感到不屑,而總想在項目中寫一些犀利的代碼,讓人看起來很NB,但結果老是和想象差太遠,代碼老是寫的不好,邏輯也不夠清晰。歸根到底,是我帶着這樣的思想去寫代碼,而忽略了編程的根本:解決問題。如今我改掉了這個壞毛病,以解決問題爲目的去編程,以簡單爲主。出乎意料的是別人有時會對我說,這裏的代碼寫得很棒。

踏實的作事,會有意想不到的收穫。

 

 

認可錯誤

不要懷疑,當別人用本身的程序或者代碼沒法運行時,首先考慮是不是本身的邏輯哪裏有問題。一來別人會以爲我謙虛,二來實際大多數狀況的確是本身的問題。

 

 

有原則,有決心

作任何事情都堅持原則,並有決心是最好的。有不少道理咱們都明白,但常常作不到,沒有任何人能幫到本身,將來也是本身爭取的。

因此,若是知道什麼是好,就儘可能去作,什麼是很差,就儘可能避免。

即便是在公司面對經理和領導,也要堅持本身的作法,一些不合理的需求應該指出或拒絕。我還年輕,大不了換一家公司,而不肯意作一個受欺壓的碼農。

 

 

我在作什麼

文章寫完了,如今來回想一下,我是在分享本身如今編程的一些習慣,總算沒偏離開始的主題。本文的思想都是來自實際工做和一些書籍,想了解更多的話,推薦閱讀《整潔代碼之道》《代碼大全》《重構》這幾本書。

若是你有一些認爲好的編程方法,不妨拿出來和你們分享一下。

相關文章
相關標籤/搜索