<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    搬磚頭

    Knocking on Heaven's Door
    posts - 34, comments - 6, trackbacks - 0, articles - 0
    原文出處:http://www.tkk7.com/chords/archive/2006/12/14/87591.html
    ------------------------
    Five Habits of Highly Profitable Software Developers

    by Robert J. Miller
    08/24/2006
    原文地址:
    http://today.java.net/pub/a/today/2006/08/24/five-habits-of-highly-profitable-developers.html



    當(dāng)今技術(shù)引領(lǐng)經(jīng)濟(jì)社會(huì)大量的需要能夠在團(tuán)隊(duì)環(huán)境中開(kāi)發(fā)出穩(wěn)定質(zhì)量的軟件開(kāi)發(fā)人員。在團(tuán)隊(duì)開(kāi)發(fā)的環(huán)境中,開(kāi)發(fā)者面對(duì)的挑戰(zhàn)就是讀懂別的開(kāi)發(fā)者寫(xiě)的軟件。本文將文章盡力幫助軟件開(kāi)發(fā)團(tuán)隊(duì)來(lái)克服這樣的困難。

    本文為了闡明了五個(gè)讓開(kāi)發(fā)團(tuán)隊(duì)變得比以往更加高效的好習(xí)慣,首先將介紹公司業(yè)務(wù)對(duì)開(kāi)發(fā)團(tuán)隊(duì)以及他們開(kāi)發(fā)出軟件的需求,接下來(lái)會(huì)解釋狀態(tài)改變邏輯和行為邏輯之間重要的區(qū)別,最后會(huì)通過(guò)顧客賬號(hào)這么一個(gè)案例來(lái)闡述這五個(gè)習(xí)慣。

    業(yè)務(wù)帶給開(kāi)發(fā)人員的需求

    公司業(yè)務(wù)團(tuán)隊(duì)的工作就是在決定將哪些對(duì)公司業(yè)務(wù)最有利的新價(jià)值可以被加到軟件中。這里的“新價(jià)值”指的是新產(chǎn)品或者是對(duì)現(xiàn)有產(chǎn)品的強(qiáng)化。換句話說(shuō)就是,業(yè)務(wù)團(tuán)隊(duì)決定什么將給公司帶來(lái)最多的錢。決定了下個(gè)新價(jià)值是什么的關(guān)鍵因素是實(shí)現(xiàn)它的成本。如果實(shí)現(xiàn)的成本超過(guò)了潛在收益,那么這個(gè)新價(jià)值就不會(huì)被加到軟件中來(lái)。

    業(yè)務(wù)團(tuán)隊(duì)要求開(kāi)發(fā)團(tuán)隊(duì)能夠盡可能低成本的,并且是在規(guī)定時(shí)間內(nèi)以及在不失去原有價(jià)值的情況下創(chuàng)造新價(jià)值。當(dāng)軟件增加了一定價(jià)值后,業(yè)務(wù)團(tuán)隊(duì)會(huì)要求一份描述現(xiàn)有軟件所能提供的價(jià)值的文檔。這個(gè)文檔將幫助他們決定下一個(gè)新價(jià)值是什么。

    軟件開(kāi)發(fā)團(tuán)隊(duì)通過(guò)創(chuàng)造出容易理解的軟件來(lái)滿足商業(yè)團(tuán)隊(duì)的需求。難以理解的軟件帶來(lái)的后果就是整個(gè)開(kāi)發(fā)過(guò)程的低效率。低效率會(huì)造成軟件開(kāi)發(fā)成本的增加,引起一些預(yù)料不到的現(xiàn)有價(jià)值的損失,開(kāi)發(fā)周期滾雪球般越拖越長(zhǎng)以及交付錯(cuò)誤的軟件文檔。通過(guò)改變業(yè)務(wù)團(tuán)隊(duì)的需求,甚至將復(fù)雜的軟件轉(zhuǎn)變成簡(jiǎn)單、容易理解的軟件,就可以提高開(kāi)發(fā)過(guò)程的效率。


    介紹關(guān)鍵概念:狀態(tài)和行為

    開(kāi)發(fā)容易理解的軟件可以從創(chuàng)建有狀態(tài)和行為的對(duì)象開(kāi)始。“狀態(tài)”是對(duì)象在調(diào)用方法前后所保存的數(shù)據(jù)。一個(gè)JAVA對(duì)象的實(shí)例變量可以暫時(shí)的保持自己的狀態(tài),并且可以隨時(shí)存放到數(shù)據(jù)存儲(chǔ)器里。這里,永久數(shù)據(jù)存儲(chǔ)器可以是數(shù)據(jù)庫(kù)或者是Web服務(wù)。“狀態(tài)變更方法”主要管理一個(gè)對(duì)象的數(shù)據(jù)存取。“行為”則是一個(gè)對(duì)象基于狀態(tài)回答問(wèn)題的能力。“行文方法”回答問(wèn)題永遠(yuǎn)不會(huì)修改狀態(tài),并且這些方法往往跟一個(gè)應(yīng)用的商業(yè)邏輯有關(guān)。


    案例研究:CustomerAccount對(duì)象

    下面這個(gè)ICustomerAccount接口定義了管理一個(gè)客戶賬號(hào)對(duì)象必須實(shí)現(xiàn)的功能。這個(gè)接口定義了可以創(chuàng)建一個(gè)新的賬號(hào),加載一個(gè)已經(jīng)存在的賬號(hào)的信息,驗(yàn)證某個(gè)賬號(hào)的用戶名和密碼,驗(yàn)證購(gòu)買時(shí)這個(gè)賬號(hào)是否是激活的。

    public?interface?ICustomerAccount?{
    ??
    //State-changing?methods
    ??public?void?createNewActiveAccount()
    ???????????????????
    throws?CustomerAccountsSystemOutageException;
    ??
    public?void?loadAccountStatus()?
    ???????????????????
    throws?CustomerAccountsSystemOutageException;
    ??
    //Behavior?methods
    ??public?boolean?isRequestedUsernameValid();
    ??
    public?boolean?isRequestedPasswordValid();
    ??
    public?boolean?isActiveForPurchasing();
    ??
    public?String?getPostLogonMessage();
    }


    習(xí)慣一:構(gòu)造器盡量少做事

    第一個(gè)應(yīng)該養(yǎng)成的喜歡就是讓類的構(gòu)造器盡量的少做些事情。理想的情況就是構(gòu)造器僅僅用來(lái)接受參數(shù)給實(shí)例變量加載數(shù)據(jù)。下面一個(gè)例子,讓構(gòu)造器做盡可能少的事情會(huì)讓這個(gè)類使用起來(lái)比較簡(jiǎn)單,因?yàn)闃?gòu)造器只是簡(jiǎn)單的給類中的實(shí)例變量賦值。

    public?class?CustomerAccount?implements?ICustomerAccount{
    ??
    //Instance?variables.
    ??private?String?username;
    ??
    private?String?password;
    ??
    protected?String?accountStatus;
    ??
    ??
    //Constructor?that?performs?minimal?work.
    ??public?CustomerAccount(String?username,?String?password)?{
    ????
    this.password?=?password;
    ????
    this.username?=?username;
    ??}

    }


    構(gòu)造器是用來(lái)創(chuàng)建一個(gè)類的實(shí)例。構(gòu)造器的名字永遠(yuǎn)是跟這個(gè)類的名字是一樣的。既然構(gòu)造器的名字無(wú)法改變,那么它就不能表達(dá)出它做的事情的含義。所以,最好是盡可能的讓構(gòu)造器少做點(diǎn)事。另一個(gè)方面,狀態(tài)變更方法和行為方法會(huì)通過(guò)自己的名字來(lái)表達(dá)出自己復(fù)雜的工作,在“習(xí)慣二:方法名要清晰的表現(xiàn)意圖”中會(huì)詳細(xì)講到。下一個(gè)例子表明,很大程度上是因?yàn)闃?gòu)造器十分的簡(jiǎn)單,更多的讓狀態(tài)變更和行為方法來(lái)完成其他的部分,使得一個(gè)軟件具有很高的可讀性。


    注:例子中“...”部分僅僅是真實(shí)情景中必須的部分,跟本文要闡述的問(wèn)題沒(méi)有關(guān)系。

    String?username?=?"robertmiller";
    String?password?
    =?"java.net";
    ICustomerAccount?ca?
    =?new?CustomerAccount(username,?password);
    if(ca.isRequestedUsernameValid()?&&?ca.isRequestedPasswordValid())?{
    ???
    ???ca.createNewActiveAccount();
    ???
    }

    相反的,如果構(gòu)造器除了給實(shí)例變量賦值以外的事情,將會(huì)使代碼很難讓人理解,并且有可能被誤用,因?yàn)闃?gòu)造器的名字沒(méi)有說(shuō)明要做的意圖。例如,下面的代碼將調(diào)用數(shù)據(jù)庫(kù)或者Web服務(wù)來(lái)預(yù)加載賬號(hào)的狀態(tài):

    //Constructor?that?performs?too?much?work!
    public?CustomerAccount(String?username,?String?password)?
    ??????????????????
    throws?CustomerAccountsSystemOutageException?{

    ??
    this.password?=?password;
    ??
    this.username?=?username;
    ??
    this.loadAccountStatus();//unnecessary?work.
    }

    //Remote?call?to?the?database?or?web?service.
    public?void?loadAccountStatus()?
    ??????????????????
    throws?CustomerAccountsSystemOutageException?{
    ??
    }

    別人可能在不知道會(huì)使用遠(yuǎn)程調(diào)的情況下使用這個(gè)構(gòu)造器,從而導(dǎo)致了以下個(gè)遠(yuǎn)程調(diào)用:

    String?username?=?"robertmiller";
    String?password?
    =?"java.net";?
    try?{
    ??
    //makes?a?remote?call
    ??ICustomerAccount?ca?=?new?CustomerAccount(username,?password);
    ??
    //makes?a?second?remote?call
    ??ca.loadAccountStatus();
    }
    ?catch?(CustomerAccountsSystemOutageException?e)?{
    ??
    }

    或者使開(kāi)發(fā)人員重用這個(gè)構(gòu)造器來(lái)驗(yàn)證用戶名和密碼,并且被強(qiáng)制的進(jìn)行了遠(yuǎn)程調(diào)用,然而這些行為方法(isRequestedUsernameValid(), isRequestedPasswordValid())根本不需要賬戶的狀態(tài):

    String?username?=?"robertmiller";
    String?password?
    =?"java.net";
    try?{
    ??
    //makes?unnecessary?remote?call
    ??ICustomerAccount?ca?=?new?CustomerAccount(username,?password);
    ??
    if(ca.isRequestedUsernameValid()?&&?ca.isRequestedPasswordValid())?{
    ????
    ????ca.createNewActiveAccount();
    ????
    ??}

    }
    ?catch?(CustomerAccountsSystemOutageException?e){
    ??
    }


    習(xí)慣二:方法名要清晰的表現(xiàn)意圖

    第二個(gè)習(xí)慣就是要讓所有的方法名字清晰的表現(xiàn)本方法要做什么的意圖。例如isRequestedUsernameValid()讓開(kāi)發(fā)人員知道這個(gè)方法時(shí)用來(lái)驗(yàn)證用戶名是否正確的。相反的,isGoodUser() 可能有很多用途:用來(lái)驗(yàn)證賬號(hào)是否是激活的,用來(lái)驗(yàn)證用戶名或者密碼是否正確,或者是用來(lái)搞清楚用戶是不是個(gè)好人。方法名表意不清,這就很難讓開(kāi)發(fā)者明白這個(gè)方法到底是用來(lái)干什么的。簡(jiǎn)單的說(shuō),長(zhǎng)一點(diǎn)并且表意清晰的方法名要比又短又表意不明的方法名好。

    表意清晰的長(zhǎng)名字會(huì)幫助開(kāi)發(fā)團(tuán)隊(duì)快速的理解他們軟件的功能意圖。更大的優(yōu)點(diǎn)在于,給測(cè)試方法也起個(gè)好名字會(huì)讓軟件現(xiàn)有的要求更加的清晰。例如,本軟件要求驗(yàn)證請(qǐng)求的用戶名和用戶密碼是不同的。使用名為testRequestedPasswordIsNotValidBecauseItMustBeDifferentThanTheUsername()的方法清晰的表達(dá)出了方法的意圖,也就是軟件要達(dá)到的要求。

    import?junit.framework.TestCase;

    public?class?CustomerAccountTest?extends?TestCase{
    ??
    public?void?testRequestedPasswordIsNotValid
    ????????BecauseItMustBeDifferentThanTheUsername()
    {
    ????String?username?
    =?"robertmiller";
    ????String?password?
    =?"robertmiller";
    ????ICustomerAccount?ca?
    =?new?CustomerAccount(username,?password);
    ????assertFalse(ca.isRequestedPasswordValid());
    ??}

    }


    這個(gè)方法簡(jiǎn)單的被命名為testRequestedPasswordIsNotValid(),或者更糟的是testBadPassword()。這兩個(gè)名字都讓人很難搞清楚這個(gè)方法是用來(lái)測(cè)試什么的。不清楚或者是模棱兩可的測(cè)試方法名會(huì)帶來(lái)生產(chǎn)力的損失。從而導(dǎo)致花費(fèi)來(lái)越多的時(shí)間來(lái)理解測(cè)試,不必要的重復(fù)測(cè)試,或者是破壞了被測(cè)試的類。

    最后,明了的方法名還能減少文檔和注釋的工作量。


    習(xí)慣三:一個(gè)對(duì)象只進(jìn)行一類服務(wù)。

    第三個(gè)喜歡就是對(duì)象只關(guān)心處理一小類獨(dú)立的服務(wù)。一個(gè)對(duì)象只處理一小部分事情將使得代碼更好讀好用,因?yàn)槊總€(gè)對(duì)象代碼量很少。更糟糕的是,重復(fù)的邏輯將花費(fèi)很多時(shí)間和成本去維護(hù)。設(shè)想一下,業(yè)務(wù)部門(mén)將來(lái)要求升級(jí)一下isRequestedPasswordValid()里的邏輯,然而有兩個(gè)不同的對(duì)象卻有著功能完全一樣但是名字不一樣的方法。這種情況下,開(kāi)發(fā)團(tuán)隊(duì)要花費(fèi)更多的時(shí)間去更新兩個(gè)對(duì)象,而不是一個(gè)。

    這個(gè)案例表明了CustomerAccount類的目的就是管理一個(gè)客戶的帳號(hào)。它首先創(chuàng)建了一個(gè)帳號(hào),然后嚴(yán)整這個(gè)帳號(hào)能否用來(lái)購(gòu)買產(chǎn)品。假設(shè)軟件要給所有購(gòu)買過(guò)10件物品以上的客戶打折。再創(chuàng)建一個(gè)接口叫ICustomerTransactions和一個(gè)叫CustomerTransactions的類,這樣會(huì)讓代碼更加易懂,并且實(shí)現(xiàn)目標(biāo)。

    public?interface?ICustomerTransactions?{
    ??
    //State-changing?methods
    ??public?void?createPurchaseRecordForProduct(Long?productId)
    ?????????????????????
    throws?CustomerTransactionsSystemException;
    ??
    public?void?loadAllPurchaseRecords()
    ?????????????????????
    throws?CustomerTransactionsSystemException;
    ??
    //Behavior?method
    ??public?void?isCustomerEligibleForDiscount();
    }

    這個(gè)新的類里面有狀態(tài)變更和行為方法,可以儲(chǔ)存客戶的交易并且判斷是否能夠打折。這個(gè)類創(chuàng)建起來(lái)十分簡(jiǎn)單,方便測(cè)試以及穩(wěn)定,因?yàn)樗鼘W⑿倪@一個(gè)目標(biāo)。一個(gè)低效率的方法是如同下面的例子一樣在ICustomerAccount接口和CustomerAccount類加上新的方法:

    public?interface?ICustomerAccount?{
    ??
    //State-changing?methods
    ??public?void?createNewActiveAccount()
    ???????????????????
    throws?CustomerAccountsSystemOutageException;
    ??
    public?void?loadAccountStatus()
    ???????????????????
    throws?CustomerAccountsSystemOutageException;
    ??
    public?void?createPurchaseRecordForProduct(Long?productId)
    ???????????????????
    throws?CustomerAccountsSystemOutageException;
    ??
    public?void?loadAllPurchaseRecords()
    ???????????????????
    throws?CustomerAccountsSystemOutageException;
    ??
    //Behavior?methods
    ??public?boolean?isRequestedUsernameValid();
    ??
    public?boolean?isRequestedPasswordValid();
    ??
    public?boolean?isActiveForPurchasing();
    ??
    public?String?getPostLogonMessage();
    ??
    public?void?isCustomerEligibleForDiscount();
    }

    ?

    就像是上面所看到的一樣,這樣使得類具有太多職責(zé),難以讀懂,甚至更容被易誤解。代碼被誤解的后果就是降低生產(chǎn)力,費(fèi)時(shí)費(fèi)力。總的來(lái)說(shuō),最好讓一個(gè)對(duì)象和它的方法集中處理一個(gè)小的工作單元。


    習(xí)慣四:狀態(tài)變更方法少含有行為邏輯

    第四個(gè)習(xí)慣是讓狀態(tài)變更方法少含有行為邏輯。混合了狀態(tài)變更邏輯和行為邏輯的代碼讓人很難理解,因?yàn)樵谝粋€(gè)地方處理了太多的事情。狀態(tài)變更方法涉及到遠(yuǎn)程調(diào)用來(lái)存儲(chǔ)數(shù)據(jù)的話很容易產(chǎn)生系統(tǒng)問(wèn)題。如果遠(yuǎn)程方法是相對(duì)獨(dú)立的,并且方法本身沒(méi)有行為邏輯,這樣診斷起狀態(tài)改變方法就會(huì)十分容易。另外一個(gè)問(wèn)題是,混合了行為邏輯的狀態(tài)代碼很難進(jìn)行單元測(cè)試。例如,getPostLogonMessage() 是一個(gè)依靠accountStatus的值的行為:

    public?String?getPostLogonMessage()?{
    ??
    if("A".equals(this.accountStatus)){
    ????
    return?"Your?purchasing?account?is?active.";
    ??}
    ?else?if("E".equals(this.accountStatus))?{
    ????
    return?"Your?purchasing?account?has?"?+
    ???????????
    "expired?due?to?a?lack?of?activity.";
    ??}
    ?else?{
    ????
    return?"Your?purchasing?account?cannot?be?"?+
    ???????????
    "found,?please?call?customer?service?"+
    ???????????
    "for?assistance.";
    ??}

    }


    loadAccountStatus()是一個(gè)使用遠(yuǎn)程調(diào)用來(lái)加載 accountStatus值的狀態(tài)改變方法。

    public?void?loadAccountStatus()?
    ??????????????????
    throws?CustomerAccountsSystemOutageException?{
    ??Connection?c?
    =?null;
    ??
    try?{
    ????c?
    =?DriverManager.getConnection("databaseUrl",?"databaseUser",?
    ????????????????????????????????????
    "databasePassword");
    ????PreparedStatement?ps?
    =?c.prepareStatement(
    ??????????????
    "SELECT?status?FROM?customer_account?"
    ????????????
    +?"WHERE?username?=???AND?password?=???");
    ????ps.setString(
    1,?this.username);
    ????ps.setString(
    2,?this.password);
    ????ResultSet?rs?
    =?ps.executeQuery();
    ????
    if?(rs.next())?{
    ??????
    this.accountStatus=rs.getString("status");
    ????}

    ????rs.close();
    ????ps.close();
    ????c.close();??
    ??}
    ?catch?(SQLException?e)?{
    ????
    throw?new?CustomerAccountsSystemOutageException(e);
    ??}
    ?finally?{
    ????
    if?(c?!=?null)?{
    ??????
    try?{
    ????????c.close();
    ???????}
    ?catch?(SQLException?e)?{}
    ????}

    ??}

    }


    單元測(cè)試 getPostLogonMessage()? 方法十分簡(jiǎn)單,只用loadAccountStatus()方法就行了。每個(gè)場(chǎng)景都可以在使用遠(yuǎn)程調(diào)用連接數(shù)據(jù)庫(kù)的情況下進(jìn)行測(cè)試。例如,如果 accountStatus 的值是E,代表過(guò)期,則getPostLogonMessage() 會(huì)如下代碼顯示一樣返回 "Your purchasing account has expired due to a lack of activity"

    public?void?testPostLogonMessageWhenStatusIsExpired(){
    ??String?username?
    =?"robertmiller";
    ??String?password?
    =?"java.net";
    ?
    ??
    class?CustomerAccountMock?extends?CustomerAccount{
    ????????
    ????
    public?void?loadAccountStatus()?{
    ??????
    this.accountStatus?=?"E";
    ????}

    ??}

    ??ICustomerAccount?ca?
    =?new?CustomerAccountMock(username,?password);
    ??
    try?{
    ????ca.loadAccountStatus();
    ??}
    ?
    ??
    catch?(CustomerAccountsSystemOutageException?e){
    ????fail(
    ""+e);
    ??}

    ??assertEquals(
    "Your?purchasing?account?has?"?+
    ?????????????????????
    "expired?due?to?a?lack?of?activity.",
    ?????????????????????ca.getPostLogonMessage());
    }

    下面這個(gè)反例將getPostLogonMessage() 的行為邏輯和loadAccountStatus()的狀態(tài)轉(zhuǎn)變都放到了一個(gè)方法里,我們不應(yīng)該這么做:

    public?String?getPostLogonMessage()?{
    ??
    return?this.postLogonMessage;
    }

    public?void?loadAccountStatus()?
    ??????????????????
    throws?CustomerAccountsSystemOutageException?{
    ??Connection?c?
    =?null;
    ??
    try?{
    ????c?
    =?DriverManager.getConnection("databaseUrl",?"databaseUser",?
    ????????????????????????????????????
    "databasePassword");
    ????PreparedStatement?ps?
    =?c.prepareStatement(
    ??????????
    "SELECT?status?FROM?customer_account?"
    ????????
    +?"WHERE?username?=???AND?password?=???");
    ????ps.setString(
    1,?this.username);
    ????ps.setString(
    2,?this.password);
    ????ResultSet?rs?
    =?ps.executeQuery();
    ????
    if?(rs.next())?{
    ??????
    this.accountStatus=rs.getString("status");
    ????}

    ????rs.close();
    ????ps.close();
    ????c.close();??
    ??}
    ?catch?(SQLException?e)?{
    ????
    throw?new?CustomerAccountsSystemOutageException(e);
    ??}
    ?finally?{
    ????
    if?(c?!=?null)?{
    ??????
    try?{
    ????????c.close();
    ???????}
    ?catch?(SQLException?e)?{}
    ????}

    ??}

    ??
    if("A".equals(this.accountStatus)){
    ????
    this.postLogonMessage?=?"Your?purchasing?account?is?active.";
    ??}
    ?else?if("E".equals(this.accountStatus))?{
    ????
    this.postLogonMessage?=?"Your?purchasing?account?has?"?+
    ????????????????????????????
    "expired?due?to?a?lack?of?activity.";
    ??}
    ?else?{
    ????
    this.postLogonMessage?=?"Your?purchasing?account?cannot?be?"?+
    ????????????????????????????
    "found,?please?call?customer?service?"+
    ????????????????????????????
    "for?assistance.";
    ??}

    }

    這個(gè)實(shí)現(xiàn)了一個(gè)沒(méi)有包含任何行為邏輯的getPostLogonMessage()行為方法,并且簡(jiǎn)單的返回一個(gè)實(shí)例變量this.postLogonMessage。這么做有三個(gè)問(wèn)題:第一,很難讓人明白"post logon message"這個(gè)嵌入到一個(gè)方法中的邏輯式怎么完成兩個(gè)任務(wù)的。第二,getPostLogonMessage()方法很難被重用,因?yàn)樗偸呛蚻oadAccountStatus()方法相關(guān)聯(lián)。最后,CustomerAccountsSystemOutageException異常將會(huì)拋出,導(dǎo)致了在給this.postLogonMessage賦值前就退出方法了。

    這個(gè)實(shí)現(xiàn)同樣創(chuàng)造了負(fù)面效應(yīng),因?yàn)橹挥袆?chuàng)建一個(gè)存在于數(shù)據(jù)庫(kù)的CustomerAccount對(duì)象,并且將賬號(hào)狀態(tài)設(shè)置成E才能進(jìn)行對(duì)getPostLogonMessage()邏輯的單元測(cè)試。結(jié)果式這個(gè)測(cè)試要進(jìn)行遠(yuǎn)程調(diào)用。這會(huì)導(dǎo)致測(cè)試的很慢,而且在改變數(shù)據(jù)庫(kù)內(nèi)容的時(shí)候很容易出意想不到的問(wèn)題。由于 loadAccountStatus()方法包含了行為邏輯,測(cè)試必須進(jìn)行遠(yuǎn)程調(diào)用。如果行為邏輯測(cè)試失敗了,測(cè)的只是那個(gè)失敗的對(duì)象行為,而不是真正的對(duì)象的行為。


    習(xí)慣五:可以任意次序調(diào)用行為方法

    第五個(gè)習(xí)慣是要保證每個(gè)行為方法之間保持著獨(dú)立。換句話說(shuō),一個(gè)對(duì)象的行為方法可以被重復(fù)或任何次序來(lái)調(diào)用。這個(gè)習(xí)慣能讓對(duì)象實(shí)現(xiàn)穩(wěn)定的行為。比如,CustomerAccount's isActiveForPurchasing()和getPostLogonMessage() 行為方法都要用到accountStatus的值。這兩個(gè)方法必須在功能上相互獨(dú)立。例如,有一個(gè)情景要求調(diào)用isActiveForPurchasing(),接著又調(diào)用了getPostLogonMessage():

    ICustomerAccount?ca?=?new?CustomerAccount(username,?password);
    ca.loadAccountStatus();
    if(ca.isActiveForPurchasing()){?
    ??
    //go?to?"begin?purchasing"?display
    ??
    ??
    //show?post?logon?message.
    ??ca.getPostLogonMessage();
    }
    ?else?{
    ??
    //go?to?"activate?account"?display??
    ??
    ??
    //show?post?logon?message.
    ??ca.getPostLogonMessage();?????
    }


    ?一個(gè)發(fā)送的情節(jié)會(huì)要求調(diào)用getPostLogonMessage()之前不必調(diào)用isActiveForPurchasing():

    ICustomerAccount?ca?=?new?CustomerAccount(username,?password);
    ca.loadAccountStatus();
    //go?to?"welcome?back"?display?

    //show?post?logon?message.
    ca.getPostLogonMessage();


    如果要求調(diào)用getPostLogonMessage()之前必須調(diào)用isActiveForPurchasing()方法,CustomerAccount 對(duì)象將無(wú)法支持第二個(gè)情景。如果兩個(gè)方法使用了 postLogonMessage 實(shí)例變量來(lái)存放兩個(gè)方法所需要的值,那么這將支持第一個(gè)情景,但不支持第二個(gè):

    public?boolean?isActiveForPurchasing()?{
    ??
    boolean?returnValue?=?false;
    ??
    if("A".equals(this.accountStatus)){
    ????
    this.postLogonMessage?=?"Your?purchasing?account?is?active.";
    ????returnValue?
    =?true;
    ??}
    ?else?if("E".equals(this.accountStatus))?{
    ????
    this.postLogonMessage?=?"Your?purchasing?account?has?"?+
    ????????????????????????????
    "expired?due?to?a?lack?of?activity.";
    ????returnValue?
    =?false;

    ??}
    ?else?{
    ????
    this.postLogonMessage?=?"Your?purchasing?account?cannot?be?"?+
    ????????????????????????????
    "found,?please?call?customer?service?"+
    ????????????????????????????
    "for?assistance.";
    ????returnValue?
    =?false;
    ??}

    ??
    return?returnValue;
    }

    public?String?getPostLogonMessage()?{
    ??
    return?this.postLogonMessage;
    }

    然而,如果兩個(gè)方法的邏輯推理是相互獨(dú)立的,那么就可以支持第二個(gè)情景了。在下面的一個(gè)例子中,postLogonMessage是getPostLogonMessage()創(chuàng)建的一個(gè)局部變量。

    public?boolean?isActiveForPurchasing()?{
    ??
    return?this.accountStatus?!=?null?&&?this.accountStatus.equals("A");
    }

    public?String?getPostLogonMessage()?{
    ??
    if("A".equals(this.accountStatus)){
    ????
    return?"Your?purchasing?account?is?active.";
    ??}
    ?else?if("E".equals(this.accountStatus))?{
    ????
    return?"Your?purchasing?account?has?"?+
    ???????????
    "expired?due?to?a?lack?of?activity.";
    ??}
    ?else?{
    ????
    return?"Your?purchasing?account?cannot?be?"?+
    ???????????
    "found,?please?call?customer?service?"+
    ???????????
    "for?assistance.";
    ??}

    }

    讓這兩個(gè)方法之間相互獨(dú)立的另一個(gè)好處是更容易理解。例如,isActiveForPurchasing()如果只是用來(lái)回答如“能否購(gòu)買”的問(wèn)題會(huì)顯得可讀性更佳,如果是用來(lái)解決“顯示登陸消息”就不那么好了。另一個(gè)好處就是測(cè)試是獨(dú)立的,讓測(cè)試更加簡(jiǎn)單和容易理解:

    public?class?CustomerAccountTest?extends?TestCase{
    ??
    public?void?testAccountIsActiveForPurchasing(){
    ????String?username?
    =?"robertmiller";
    ????String?password?
    =?"java.net";

    ????
    class?CustomerAccountMock?extends?CustomerAccount{
    ??????
    ??????
    public?void?loadAccountStatus()?{
    ????????
    this.accountStatus?=?"A";
    ??????}

    ????}

    ????ICustomerAccount?ca?
    =?new?CustomerAccountMock(username,?password);
    ????
    try?{
    ??????ca.loadAccountStatus();
    ????}
    ?catch?(CustomerAccountsSystemOutageException?e)?{
    ??????fail(
    ""+e);
    ????}

    ????assertTrue(ca.isActiveForPurchasing());?
    ??}
    ?
    ??
    ??
    public?void?testGetPostLogonMessageWhenAccountIsActiveForPurchasing(){
    ????String?username?
    =?"robertmiller";
    ????String?password?
    =?"java.net";

    ????
    class?CustomerAccountMock?extends?CustomerAccount{
    ??????
    ??????
    public?void?loadAccountStatus()?{
    ????????
    this.accountStatus?=?"A";
    ??????}

    ????}

    ????ICustomerAccount?ca?
    =?new?CustomerAccountMock(username,?password);
    ????
    try?{
    ??????ca.loadAccountStatus();
    ????}
    ?catch?(CustomerAccountsSystemOutageException?e)?{
    ??????fail(
    ""+e);
    ????}

    ????assertEquals(
    "Your?purchasing?account?is?active.",
    ??????????????????????????????ca.getPostLogonMessage());
    ??}

    }


    總結(jié)

    上述的五種習(xí)慣會(huì)幫助開(kāi)發(fā)團(tuán)隊(duì)創(chuàng)造出方便閱讀、理解和修改的軟件。如果開(kāi)發(fā)團(tuán)隊(duì)僅僅是想快速的創(chuàng)造價(jià)值而不考慮將來(lái)的規(guī)劃,他們軟件的實(shí)現(xiàn)將會(huì)耗費(fèi)越來(lái)越多的成本。當(dāng)這些開(kāi)發(fā)團(tuán)隊(duì)要審查軟件來(lái)理解和修改時(shí),不可避免的會(huì)遭到自己寫(xiě)的壞代碼的報(bào)復(fù)。如果軟件十分難以理解,在增加新價(jià)值的時(shí)候會(huì)花費(fèi)巨大的代價(jià)。然而,一旦開(kāi)發(fā)團(tuán)隊(duì)將良好的習(xí)慣運(yùn)用到開(kāi)發(fā)實(shí)踐中,他們會(huì)以最低的成本為業(yè)務(wù)提供新價(jià)值。

    只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 色se01短视频永久免费| 亚洲国产美国国产综合一区二区 | 免费一级做a爰片久久毛片潮| 日韩免费视频在线观看| 亚洲精品国产摄像头| 成人在线视频免费| 亚洲高清一区二区三区| 精品熟女少妇AV免费观看| 亚洲综合伊人制服丝袜美腿| 香蕉97超级碰碰碰免费公| 亚洲av乱码一区二区三区香蕉| 免费精品人在线二线三线区别| ASS亚洲熟妇毛茸茸PICS| 成年女人色毛片免费看| 中文日韩亚洲欧美制服| 成人毛片视频免费网站观看| 亚洲免费综合色在线视频| 永久在线毛片免费观看| 国产亚洲视频在线| 五月天婷亚洲天综合网精品偷| 男女作爱免费网站| 久久亚洲2019中文字幕| a毛片在线还看免费网站| 亚洲AV无码久久精品成人| 久久国产乱子伦精品免费不卡| 亚洲精品国产专区91在线| 成人免费大片免费观看网站| 亚洲国产激情在线一区| 四虎免费在线观看| 国产精品亚洲二区在线| 亚洲午夜AV无码专区在线播放| 中文字幕免费在线观看动作大片| 久久九九亚洲精品| 18禁成人网站免费观看| 在线观看亚洲AV每日更新无码| 日本特黄a级高清免费大片| 免费国产a理论片| 亚洲精品无码av人在线观看| 久久ww精品w免费人成| 中文字幕亚洲码在线| 免费中文字幕在线|