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

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

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

    kapok

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

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

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

     

    模式羅漢拳: Immutable模式與string類的實現

    透明
    梗概

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

    實現方法:在對象構造完成以后就完全禁止改變任何狀態信息。如果需要改變狀態,則生成一個狀態與原對象不同的新對象。

    場景

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

    例1:Position的設計

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

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

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

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

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



    圖1 Immutable模式的類圖

    效果
    • 不再需要協調狀態修改的代碼,也不再需要協調任何同步代碼。
    • 生成了更多的對象。增加了對象生成和銷毀的開銷。
    實現

    Immutable模式的實現主要有以下兩個要點:

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

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

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



    圖2 Read Only Object模式

    Immutable模式與string類的實現策略

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

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

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

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

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

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

    代碼示例

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

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

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

    相關模式

    經常會使用Abstract Factory模式[GOF95]來創建新的對象。

    大量的對象經常通過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. 中譯本:《設計模式:可復用面向對象軟件的基礎》,李英軍等譯,機械工業出版社,2000 年9月。

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

    posted on 2005-05-09 16:38 笨笨 閱讀(744) 評論(0)  編輯  收藏 所屬分類: J2EEALL
    主站蜘蛛池模板: 午夜老司机免费视频| 国产午夜免费福利红片| 亚洲视频在线观看免费视频| 免费人妻无码不卡中文字幕系 | 永久亚洲成a人片777777| 花蝴蝶免费视频在线观看高清版| 99视频有精品视频免费观看| 麻豆狠色伊人亚洲综合网站 | 岛国精品一区免费视频在线观看| 亚洲激情中文字幕| 免费无码又爽又高潮视频| 亚洲国产精品xo在线观看| 免费在线观看理论片| 国产精品无码亚洲精品2021 | 亚洲国产a级视频| 亚洲一区免费观看| 国产精品亚洲精品爽爽| 亚洲精品私拍国产福利在线| 青青青国产色视频在线观看国产亚洲欧洲国产综合 | 亚洲国产精品人久久电影| 亚洲国产精品成人AV无码久久综合影院 | 亚洲熟妇成人精品一区| 国产精品免费网站| 亚洲av不卡一区二区三区| 日本特黄特色aa大片免费| 羞羞网站免费观看| 国产免费爽爽视频免费可以看| 亚洲精品免费观看| 无码AV动漫精品一区二区免费 | 免费一级毛片在线播放视频免费观看永久 | 亚洲GV天堂GV无码男同| 亚洲综合网美国十次| 18禁黄网站禁片免费观看不卡| 亚洲资源最新版在线观看| 中文国产成人精品久久亚洲精品AⅤ无码精品 | 亚洲精品综合久久| 一本到卡二卡三卡免费高| 亚洲日本VA中文字幕久久道具| 亚洲一区二区中文| 亚洲午夜国产精品无码| 99爱在线精品视频免费观看9|