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

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

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

    Sky's blog

    我和我追逐的夢

    常用鏈接

    統計

    其他鏈接

    友情鏈接

    最新評論

    一個因參數定義不合理造成的滑稽錯誤引發的思考

        這是一個真實案例,本周在工作中發現的,案例情況比較極端,因此顯得很滑稽很搞笑。但是深入一下,還是有些東西值得思考。

        先來看這個案例,在性能優化的過程中,通過thread dump發現有非常多的線程都在執行同一個數據庫訪問。而按照分析,在cache開啟的情況下應該只訪問一次才是,后面的數據庫訪問都是不應該的。

        隨即跟蹤到問題代碼:

        //1. get pk as method parameter
        public TrafficProfile createTrafficProfile(
            
    long serviceCapabilityPrimaryKey, String serviceProviderId,                             
            String applicationId) throws NotFoundException {
     
            // 2. do database query to get serviceCapabilityProfile by pk
            ServiceCapabilityProfile serviceCapabilityProfile = new ServiceCapabilityProfilePreLoadFullSerializableImpl(getContext(),
                    serviceCapabilityPrimaryKey);
           
    // 3. generate key using obj serviceCapabilityProfile
            String key = buildTrafficProfileCacheKey(serviceProviderId, applicationId, serviceCapabilityProfile);
            TrafficProfile trafficProfile = (TrafficProfile) trafficProfileCache.get(key);
     
            //5. found in cache and return
            if ((trafficProfile != null)) {                                                            
                return trafficProfile;
            }
     
            trafficProfile 
    = new TrafficProfilePreLoadFullSerializableImpl(getContext(), serviceCapabilityProfile,
                    serviceProviderId, applicationId);
            trafficProfileCache.put(key, trafficProfile);
            
    return trafficProfile;
        }
     
        //4. notice: in fact only pk is used 
        private String buildTrafficProfileCacheKey(String serviceProviderId, String applicationId,
                ServiceCapabilityProfile serviceCapabilityProfile) {
            
    return serviceCapabilityProfile.getServiceCapabilityPrimaryKey() + "," + serviceProviderId + ","               
                    + applicationId;
        }

        因此可以看到,如果cache有效,我們其實只需要一個pk就可以組合出key從而從cache中得到保存的trafficProfile對象。但是現在在我們的代碼中,為了得到key,我們進行了一個從pk -> serviceCapabilityProfile 對象的數據庫查詢,而在使用這個serviceCapabilityProfile  對象的函數中,很驚訝的發現,其實這里真正用到的不過是一個pk而且,而這個pk我們本來就持有,何須去數據庫里跑一回?
     
    pk ---->  get serviceCapabilityProfile from database by pk ---> get pk by serviceCapabilityProfile.getServiceCapabilityPrimaryKey();

        讓我們來看看為什么會犯下如此可笑的錯誤,隨即在這個類中我們找到了另外一個createTrafficProfile():

    // parameter is serviceCapabilityProfile obj
    public TrafficProfile createTrafficProfile(
            ServiceCapabilityProfile serviceCapabilityProfile,                                                                     

            String serviceProviderId, String applicationId)
            
    throws NotFoundException {
     
            // pass to buildTrafficProfileCacheKey() is obj, not pk
            String key = buildTrafficProfileCacheKey(serviceProviderId, applicationId, serviceCapabilityProfile);                   


        現在原因就很清楚了:在方法buildTrafficProfileCacheKey()中,實際只需要一個long類型的pk值,但是在它的方法參數定義中,它卻要求傳入一個serviceCapabilityProfile 的對象。

        可以想象一下這個代碼開發的過程:

    1. 第一個人先增加了以serviceCapabilityProfile對象為參數的createTrafficProfile()方法
    2. 他創建了buildTrafficProfileCacheKey()方法,因為手頭就有serviceCapabilityProfile對象,因此他選擇了將整個對象傳入
    3. 這兩個函數工作正常,雖然這個參數傳遞的有點感覺不大好,但至少沒有造成問題

    4. 后來,另外一個人來修改這個代碼,他添加了使用long serviceCapabilityPrimaryKey的createTrafficProfile()方法
    5. 他試圖調用buildTrafficProfileCacheKey()方法,然后發現這個方法需要一個serviceCapabilityProfile 對象
    6. 他不得不進行一次數據庫訪問來獲取整個對象數據......
     
        從這個案例中,我們可以看到,一個含糊的參數是如何導致我們最終犯錯的 ^0^

        這個錯誤的修改當然非常簡單,將buildTrafficProfileCacheKey()方法的參數調整為傳入long類型的pk就解決了問題。
     
        在日常代碼中,我們有非常多的大對象諸如“****DTO/context/profile”,而它們經常被作為參數在代碼之間傳遞。因此需要小心:
     
    1. 當定義一個類似buildTrafficProfileCacheKey()的方法時
        盡量將接口的參數簡單化,如果我們確認只是需要使用到某個大對象的一兩個簡單屬性,請將方法定義為簡單類型,不需要傳入整個對象。
        或者在方法上通過javadoc說明我們只需要這個對象的某個或某幾個屬性。

    2. 當調用類似buildTrafficProfileCacheKey()的方法時
        需要稍微謹慎一些,進去目標方法,看看代碼實現,到底是需要什么數據,是否真的需要整個對象從而導致我們需要進行數據庫查詢這種的重量級操作。
        例如上面的例子,如果原有buildTrafficProfileCacheKey()的方法不容許修改,那么我們大可以new 一個serviceCapabilityProfile 對象,然后setPK()來解決,比訪問數據庫快捷多了。

        前面提到說這個案例有點"極端",這里的極端指的是buildTrafficProfileCacheKey()方法本身就在這個類之中,代碼量也非常少,意圖非常明確,本來應該很容易被發現的。因此犯錯的情況顯得比較可笑,但是我們推開來想一想,問題似乎沒有這么簡單了:如果buildTrafficProfileCacheKey()中的代碼比較復雜,可能還通過調用其他的類從而將對serviceCapabilityProfile對象的時候的代碼邏輯轉移,惡劣的情況下可能還有多層調用,甚至出現接口抽象實際代碼運行時注入等復雜場景,再假設我們沒有辦法直接看到最終的使用代碼,我們無法知道原來底層只是需要一個pk而已!那么這個問題就一點都不可笑,上面這個白白訪問一次數據庫的錯誤一定會再次發生,因為上層調用者不知道到底需要什么數據,只好整個對象全給!何況通常上層都有良好的代碼封裝,通過一個pk獲取一個對象這種事情,可能只需要一兩行代碼調用就搞定,于是我們很可能輕松自如的,一腳踩進坑里!

        所以說想復雜點問題就變得嚴峻起來:底層代碼的實現者,需要如何設計接口參數,才能準確的告知上層調用者,到底哪些數據是真實需要的?上面的案例中將參數簡單的簡化為只傳入一個pk值就明確的達到了目標,對調用者來說足夠清晰明確。但是我們考慮一下復雜場景:如果底層的實現邏輯沒有這么簡單明確,底層代碼的實現者可能擔心未來的實現邏輯會發生更改,比如需要serviceCapabilityProfile的其他數據,因此為了保持接口穩定,底層代碼的實現者一定會傾向于使用serviceCapabilityProfile對象作為參數從而保留未來不需要修改接口/函數定義就可以擴展的自由。不經意間,挖了一個坑...

        我們似乎又回到了原來犯錯的軌道中,那個看似搞笑的錯誤似乎又在對我們揮手微笑......
        只是現在,我頗有點笑不起來了:下一次,如果我面對一個函數/接口,要求傳入一個大對象,我手頭只有一個pk,還有一個現成的函數可以一行代碼就搞定查詢,我要如何才能擋住誘惑?


    posted on 2010-04-17 10:22 sky ao 閱讀(1986) 評論(3)  編輯  收藏 所屬分類: 雜談

    評論

    # re: 一個因參數定義不合理造成的滑稽錯誤引發的思考 2010-04-17 11:32 俏物悄語

    我們似乎又回到了原來犯錯的軌道中,那個看似搞笑的錯誤似乎又在對我們揮手微笑  回復  更多評論   

    # re: 一個因參數定義不合理造成的滑稽錯誤引發的思考 2010-04-19 17:20 wueddie

    我在項目中看到了相同的代碼,當時我只是以為作者可能覺得傳遞DDO參數比用long primaryKey 更加OO。

    從我的角度看,沒有辦法理解為什么不用long primaryKey,而要去用大而無當的DDO參數。  回復  更多評論   

    # re: 一個因參數定義不合理造成的滑稽錯誤引發的思考 2010-04-19 17:28 sky ao

    帖子中的原話:"為了保持接口穩定,底層代碼的實現者一定會傾向于使用serviceCapabilityProfile對象作為參數從而保留未來不需要修改接口/函數定義就可以擴展的自由"

    我是這么猜測的,從我自己的角度考慮,讓我不傳簡單的pk而是傳遞一個object,我只能想到上面這個理由。  回復  更多評論   

    主站蜘蛛池模板: 亚洲国产精彩中文乱码AV| 2048亚洲精品国产| 亚洲日产2021三区在线| 免费看又黄又无码的网站| 亚洲综合校园春色| 思思久久99热免费精品6| 四虎影库久免费视频| 久久亚洲精品高潮综合色a片| 久久精品一区二区免费看| 欧洲美熟女乱又伦免费视频| 中文字幕亚洲无线码a| 国产精品1024在线永久免费| 好吊妞在线成人免费| 亚洲AV日韩精品久久久久久| 国产成人一区二区三区视频免费| 亚洲av日韩综合一区在线观看| 免费国产黄网站在线观看可以下载| 亚洲狠狠ady亚洲精品大秀| 99视频在线观看免费| 亚洲AV无码久久精品成人 | 精品无码人妻一区二区免费蜜桃| 久久久亚洲欧洲日产国码二区 | 亚洲韩国—中文字幕| 成年人网站免费视频| 亚洲国产精品国自产拍电影| 91久久精品国产免费直播| 色在线亚洲视频www| www亚洲精品少妇裸乳一区二区| 亚洲中文无码线在线观看| 99re6在线精品免费观看| 亚洲美女视频网址| h视频在线观看免费网站| 亚洲AV无码一区二区三区电影| 亚洲综合精品网站| 免费成人激情视频| 国产精品亚洲综合一区在线观看 | 亚洲综合激情九月婷婷| 国产成人免费ā片在线观看| 亚洲爆乳无码精品AAA片蜜桃| 成人免费网站在线观看| 国产精品hd免费观看|