Separate Query from Modifier (將查詢函數和修改函數分離)

Summary 某個函數即返回對象狀態值,又修改對象狀態。創建兩個不一樣的函數,其中一個負責查詢,另外一個負責修改。                                               java

動機:程序員

  若是某個函數只是向你提供一個值,沒有任何看得見的反作用,那麼這是個頗有價值的東西。你能夠任意調用這個函數,也能夠把調用動做搬到函數的其餘地方。簡而言之,須要操心的事情少多了。數組

明確表現出「有反作用」與「無反作用」兩種函數之間的差別,是個很好的想法。下面是一條好規則:任何有返回值的函數,都不該該有看獲得的反作用。有些程序員甚至將此做爲一條必須遵照的規則。就像對待任何東西同樣,咱們並不須要絕對遵照它,不過仍是要儘可能遵照。緩存

若是你遇到一個「既有返回值又有反作用」的函數,就應該試着將查詢動做從修改動做中分割出來。安全

你也許已經注意到了:咱們使用「看獲得的反作用」這種說法。有一種常見的優化辦法是:將查詢所得結果緩存與某個字段中,這麼一來後續的重複查詢就能夠大大加快速度。雖然這種作法改變了對象的狀態,但這一修改是察覺不到的,由於不管如何查詢,你老是得到相同結果。多線程

作法:併發

1.新建一個查詢函數,令它返回的值與原函數相同。函數

à觀察原函數,看它返回什麼東西。若是返回的是一個臨時變量,找出臨時變量的位置。測試

2.修改原函數,令它調用查詢函數,並返回得到的結果。優化

à原函數中的每一個return語句都應該像這樣:return new Query(),而不該該返回其餘東西。

à若是調用者將返回值賦給了一個臨時變量,你應該可以去除這個臨時變量。

3.編譯,測試。

4.將調用原函數的代碼改成調用查詢函數。而後,在調用查詢函數的那一行以前,加上對原函數的調用。每次修改後,編譯並測試。

5.將原函數的返回值改成void,並刪掉其中全部的return語句。

        範例:

        有這樣一個函數:一旦有人入侵安全系統,它會告訴咱們入侵者的名字,併發送一個警報。若是入侵者不止一個,也只發送一條警報:

String foundMiscreant(String[] people){
    for(int i=0; i < people.lenght; i++){
        if(people[i].equals("Don")){
            sendAlert();
            return "Don";
        }
        if(people[i].equals("John")){
            sendAlert();
            return "John";
        }
    }
    return "";
}

改函數被下列代碼調用

void checkSecurity(String[] people){
    String found = foundMiscreant(people);
    someLaterCode(found);
}

爲了將查詢動做和修改動做分開,首先創建一個適當的查詢函數,使其與修改函數返回相同的值,但不形成任何反作用:

String foundPerson(String[] people){
    for(int i=0; i < people.lenght; i++){
        if(people[i].equals("Don")){
            return "Don";
        }
        if(people[i].equals("John")){
            return "John";
        }
    }
    return "";
}

而後逐一替換原函數內全部的return語句,改調用新建的查詢函數。每次替換後,編譯並測試。這一步完成以後,原函數以下所示:

String foundMiscreant(String[] people){
    for(int i=0; i < people.lenght; i++){
        if(people[i].equals("Don")){
            sendAlert();
            return foundPerson(people);
        }
        if(people[i].equals("John")){
            sendAlert();
            return foundPerson(people);
        }
    }
    return foundPerson(people);
}

如今,修改調用者,將本來單一調用動做替換爲兩個調用:先調用修改函數,而後調用查詢函數:

void checkSecurity(String[] people){
    foundMiscreant(people);
    String found = foundPerson(people);
    someLaterCode(found);
}

全部調用都替換完畢後,就能夠將修改函數的返回值改成void

void foundMiscreant(String[] people){
    for(int i=0; i < people.lenght; i++){
        if(people[i].equals("Don")){
            sendAlert();
            return;
        }
        if(people[i].equals("John")){
            sendAlert();
            return;
        }
    }
}

如今,爲原函數改個名稱可能會更好些:

void sendAlert(String[] people){
    for(int i=0; i < people.lenght; i++){
        if(people[i].equals("Don")){
            sendAlert();
            return;
        }
        if(people[i].equals("John")){
            sendAlert();
            return;
        }
    }
}

固然,這種狀況下,咱們獲得了大量重複代碼,由於修改函數之中使用了與查詢函數相同的代碼。如今咱們能夠對修改函數實施Substitute Algorithm,設法讓它再簡潔一些:

void sendAlert(String[] people){
    if(!foundPerson(people).equals("")){
        sendAlert();
    }
}

併發問題:

   若是你在一個多線程系統工做,確定知道這樣一個重要的慣用手法:在同一個動做中完成檢查和賦值。這是否和Separate Query from Modifier互相矛盾呢?其實不,但你須要作一些額外工做。將查詢動做和修改動做分開來任然是頗有價值的。但你須要保留第三個函數,並聲明爲synchronized。若是查詢函數和修改函數未被聲明爲synchronized,那麼你還應該將它們的可見範圍限制在包級別或private級別。這樣,你就能夠擁有一個安全、同步的操做,它由兩個較易理解的函數組成。這兩個較低層函數也能夠用於其餘場合。

相關文章
相關標籤/搜索