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

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

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

    kapok

    垃圾桶,嘿嘿,我藏的這么深你們還能找到啊,真牛!

      BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
      455 隨筆 :: 0 文章 :: 76 評論 :: 0 Trackbacks

    http://www.c-view.org/journal/005/pattern_gigix.htm

     

    模式羅漢拳: Immutable模式與string類的實現(xiàn)

    透明
    梗概

    禁止改變對象的狀態(tài),從而增加共享對象的堅固性、減少對象訪問的錯誤,同時還避免了在多線程共享時進行同步的需要。

    實現(xiàn)方法:在對象構(gòu)造完成以后就完全禁止改變?nèi)魏螤顟B(tài)信息。如果需要改變狀態(tài),則生成一個狀態(tài)與原對象不同的新對象。

    場景

    假設(shè)你正在為一家游戲公司開發(fā)一個和外太空、宇宙飛船有關(guān)的游戲,當然你有必要用某種方式來表示一艘宇宙飛船(不管它是屬于地球人的還是屬于外星人的)所處的位置。很自然的,你決定編寫一個Position類。從一個Position對象應(yīng)該可以查詢到當前位置的x坐標和y坐標(我們的游戲比較簡單,二維地圖,呵呵),還應(yīng)該可以根據(jù)輸入的偏移量得到新的位置。很正確的設(shè)計,不是嗎?(見例1)

    例1:Position的設(shè)計

    class Position{
        private:
           int x, y;     //簡單點,用整型數(shù)來表示坐標
        public:
           Position(int x, int y){  //ctor需要兩個參數(shù)。
               this->x = x;
               this->y = y;
           }
           int getX( ){ return x; }
           int getY( ){ return y; }
           void Offset(int offX, int offY){  //根據(jù)偏移量得到新的位置
               x+=offX;
               y+=offY;
           }
    }
    
    但是,如果我們的Position需要在多線程環(huán)境下使用,它能保證線程安全嗎?答案是很明顯的No!如果兩條線程同時調(diào)用同一個Position對象的Offset函數(shù),你就無法保證得到的結(jié)果是什么了。所以,為了保證線程安全,也許你還會想給Offset函數(shù)加上同步機制——麻煩了!

    換個角度想想怎么樣?假如我們根本不讓Offset函數(shù)修改Position的內(nèi)容?假如我們讓Offset函數(shù)生成一個新的Position對象?如果是這樣,Position對象就已經(jīng)是線程安全的了——它沒有任何“寫”操作,而沒有寫操作的類是不需要同步的。于是我們這樣做了,并且很輕松的得到了一個線程安全的Position類。(見例2)

    例2:線程安全的Position類(這里只展示Offset函數(shù))

    Position Position::Offset(int offX, int offY){  //根據(jù)偏移量得到新的位置
        return Position(x+offX, y+offY);
    }
    
    約束
    • 你有一個天性被動的類。這個類的實例不需要改變自己的狀態(tài)。同時這個類的實例還被其他多個對象共享。
    • 正確協(xié)調(diào)被共享的對象的狀態(tài)改變非常困難。當一個對象的狀態(tài)發(fā)生改變時,所有使用它的對象都應(yīng)該得到通知。這造成了對象之間的緊耦合。
    • 在多線程共享時,還需要使用同步機制來保證線程安全性。
    解決方案

    為了避免狀態(tài)改變帶來的諸多麻煩,不允許對實例的狀態(tài)做任何修改。具體的做法就是:不在類的公開接口中出現(xiàn)任何可以修改對象狀態(tài)的方法,只出現(xiàn)狀態(tài)讀取方法。如果client需要不同的狀態(tài),就生成一個新的對象。(見圖1)



    圖1 Immutable模式的類圖

    效果
    • 不再需要協(xié)調(diào)狀態(tài)修改的代碼,也不再需要協(xié)調(diào)任何同步代碼。
    • 生成了更多的對象。增加了對象生成和銷毀的開銷。
    實現(xiàn)

    Immutable模式的實現(xiàn)主要有以下兩個要點:

    1.除了構(gòu)造函數(shù)之外,不應(yīng)該有其它任何函數(shù)(至少是任何public函數(shù))修改任何成員變量。
    2.任何使成員變量獲得新值的函數(shù)都應(yīng)該將新的值保存在新的對象中,而保持原來的對象不被修改。

    在“效果”中我已經(jīng)講到:Immutable模式會大大提高對象生成和銷毀的頻率。因此,在C++中實現(xiàn)Immutable模式時,還必須特別注意對象的生存周期。你可以嘗試用智能指針[Meyers96, Item28]來幫助你處理對象的銷毀問題,但是無論如何你都必須仔細檢查以確保沒有內(nèi)存泄漏——如果每艘飛船的每次移動都會造成內(nèi)存泄漏,你的游戲該是多么糟糕!

    此外,Immutable模式還有一種變體:Read Only Object模式。它的做法是:當一個類的對象對于某些client可寫、某些client不可寫時,讓這個類實現(xiàn)一個ReadOnly接口。然后讓可寫的client直接訪問對象,而讓不可寫的client通過ReadOnly接口訪問該對象,從而實現(xiàn)了不同的讀寫權(quán)限控制。(如圖2所示)



    圖2 Read Only Object模式

    Immutable模式與string類的實現(xiàn)策略

    如果你也讀過[Meyers96],我想你一定對那個應(yīng)用在String類上的COW(Copy-On-Write)策略[Meyers96, Item29]印象深刻。COW策略是“l(fā)azy evaluation”的發(fā)展形式。如果對String類的寫操作數(shù)量很少,那么COW策略將大大提高整個String類的效率,并大大降低空間開銷。

    可是你知道嗎?在STL中的string類并沒有采用COW策略,從例3就可以看出這一點。為什么?為什么這么好的策略沒有得到采用?相信你從[Meyers96]中便可發(fā)現(xiàn):實際在String類上實現(xiàn)COW策略是如此復雜。更何況我們還必須考慮線程安全的問題。我完全有理由認為:正是因為考慮到這些復雜的情況,STL的實現(xiàn)者們才最終決定用一個比較低效但是安全的實現(xiàn)方案。

    例3:STL中的string::operator=和string::operator[]

    //下面代碼出自SGI STL 2000年6月8日版本
    //為了幫助讀者理解,我做了些微改動,并在關(guān)鍵位置加上注釋
    
    //如果使用COW策略,operator=應(yīng)該不做內(nèi)容復制,而是進行引用計數(shù)
    string& string::operator=(const string& s) {
        if (&s != this) 
          assign(s.begin(),s.end()); // 這里的operator=只是簡單的內(nèi)容復制而已
        return *this;
    }
    
    //如果使用COW策略,const的operator[]和非const的operator[]應(yīng)該不同
    //但是這里兩個operator[]完全相同
    const char & string::operator[](int n) const
        { return *(_M_start + n); }     //_M_start是字符數(shù)組的起始位置
    char & string::operator[](int n)
        { return *(_M_start + n); }
    

    看到這些,我不能不開始猜想:為什么STL的設(shè)計者們一定要保留這些給他們造成麻煩的“修改函數(shù)”(即可以修改string內(nèi)容的函數(shù))?我想,這是因為他們希望讓string的行為方式盡量接近于C語言的char *型字符串。不然,我真的想不出其他任何保留operator[]的理由。

    那么,如果不必非要讓string類的行為方式接近char *型字符串,如果string類的讀操作應(yīng)用頻率遠遠大于寫操作(在實際應(yīng)用中這是很常見的),你會考慮如何實現(xiàn)一個string類?啊,也許你已經(jīng)想到了:Immutable模式。你可以很舒服的使用[Meyers96]教你的引用計數(shù)方法來節(jié)約存儲空間,你不必再擔心寫操作的同步問題或別的什么,因為已經(jīng)沒有寫操作。任何改變字符串內(nèi)容的操作都將得到一個新的string對象。而對象生存期管理和存儲空間管理這兩個大問題也因為Immutable模式的引入而大大簡化,你完全可以參照[Meyers96]第183頁到第189頁的內(nèi)容自己來解決它們。

    代碼示例

    我用了一天的時間,做了一個簡單的ImmutableString實現(xiàn)。其中實現(xiàn)細節(jié)用了Proxy類[Meyers96],并參考了COM的引用計數(shù)規(guī)則[Pan99]。在這個例子中,讀者可以感覺到:Immutable模式大大簡化了共享空間的字符串類型的實現(xiàn),并為其中的一些方法(比如subString)的實現(xiàn)提供了非常大的便利。本來我想把代碼放在文章里面,但是時間和空間受限,最后決定放棄。

    在該代碼中,我做了一個簡單的效率測試:反復進行字符串對象的賦值(operator=)操作。結(jié)果表明:ImmutableString的效率比std::string高出了一倍左右。假如你的業(yè)務(wù)就是不斷的讀取數(shù)據(jù)庫、不斷的賦值、不斷的輸出,而不對字符串進行修改,那么ImmutableString的效率提升是非常可觀的。

    該示例代碼在VC .NET下編譯通過。

    相關(guān)模式

    經(jīng)常會使用Abstract Factory模式[GOF95]來創(chuàng)建新的對象。

    大量的對象經(jīng)常通過Flyweight模式[GOF95]被共享。

    參考書目

    [Meyers96] Scott Meyers, More Effective C++, Addison-Wesley, 1996.

    [GOF95] Erich Gamma etc., Design Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley, 1995. 中譯本:《設(shè)計模式:可復用面向?qū)ο筌浖幕A(chǔ)》,李英軍等譯,機械工業(yè)出版社,2000 年9月。

    [PAN99] 潘愛民,《COM原理與應(yīng)用》,清華大學出版社,1999年11月。

    posted on 2005-05-09 16:38 笨笨 閱讀(744) 評論(0)  編輯  收藏 所屬分類: J2EEALL
    主站蜘蛛池模板: 国产成人自产拍免费视频| 亚洲最大天堂无码精品区| 72pao国产成视频永久免费| 免费一级毛片在线播放不收费| 伊人久久亚洲综合影院首页| 最近中文字幕mv免费高清视频7 | 91亚洲精品自在在线观看| 84pao强力永久免费高清| 亚洲国产片在线观看| 成人性生免费视频| 亚洲国产精品无码第一区二区三区| 最近中文字幕mv免费高清电影| 亚洲不卡影院午夜在线观看| 四虎影院免费视频| 污视频网站在线观看免费| 丁香五月亚洲综合深深爱| 无码国产精品一区二区免费式芒果| 亚洲电影一区二区三区| 久久精品免费一区二区喷潮| 亚洲AV无码一区二区三区久久精品| 又爽又黄无遮挡高清免费视频 | 亚洲精品无码久久久久A片苍井空| 国产免费啪嗒啪嗒视频看看| 一日本道a高清免费播放| 亚洲毛片在线观看| 狼友av永久网站免费观看| yellow视频免费在线观看| 亚洲性天天干天天摸| 毛片免费观看网站| 国产成人1024精品免费| 亚洲美女大bbbbbbbbb| 欧洲精品免费一区二区三区| 国产99久久久久久免费看| 亚洲综合婷婷久久| 国产裸模视频免费区无码| 久久久久女教师免费一区| 亚洲国产成人精品电影| 亚洲阿v天堂在线2017免费| 午夜理伦剧场免费| 国产亚洲男人的天堂在线观看 | 亚洲最大在线观看|