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

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

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

    Chan Chen Coding...

    Eleven: Memento Design Pattern

    Refer to:http://blog.sina.com.cn/s/blog_4a2100f8010142n1.html
    1.1.1.概述
    備忘錄是一個(gè)非常容易理解的模式,只需要看一下后面的客戶端測試代碼就能明白備忘錄模式的用意。備忘錄模式就是用來保存對象的某一個(gè)狀態(tài),這樣在一定時(shí)候,可以通過備忘錄來恢復(fù)對象的狀態(tài)。
    針對上面所說的用意,實(shí)現(xiàn)起來就需要一個(gè)類來保存這個(gè)狀態(tài),這個(gè)類就是備忘錄類。鑒于備忘錄很容易理解,就直接看后面的代碼實(shí)現(xiàn)吧。
    1.1.2.代碼實(shí)現(xiàn)
    在代碼實(shí)現(xiàn)中,備忘錄模式一共有三個(gè)類,Originator類是原始的對象,正是這個(gè)對象的狀態(tài)需要備忘,這樣是為了在一個(gè)時(shí)間,這個(gè)對象可以恢復(fù)到備忘的狀態(tài)。還有一個(gè)類是Memento,這個(gè)是備忘錄,用于保存Originator類的狀態(tài)。還有一個(gè)類CareTaker用來管理備忘錄。
    首先看Originator的代碼實(shí)現(xiàn)。
        /// <summary>
        /// 用于創(chuàng)建備忘錄的原發(fā)器,記錄當(dāng)前的內(nèi)部狀態(tài)
        /// </summary>
        public class Originator
        {
            /// <summary>
            /// 當(dāng)前的狀態(tài)
            /// </summary>
            private string state;
     
            /// <summary>
            /// 創(chuàng)建一個(gè)當(dāng)前狀態(tài)的備忘錄
            /// </summary>
            /// <returns></returns>
            public Memento CreateMemento()
            {
                return new Memento(this.state);
            }
     
            /// <summary>
            /// 恢復(fù)當(dāng)前狀態(tài)為備忘錄所保存的狀態(tài)
            /// </summary>
            /// <param name="memento"></param>
            public void RestoreMemento(Memento memento)
            {
                this.state = memento.GetState();
            }
     
            public string GetState()
            {
                return this.state;
            }
     
            public void SetState(string state)
            {
                this.state = state;
            }
     
        }
    Originator類具有一個(gè)狀態(tài)屬性,這個(gè)屬性代表了Originator對象的實(shí)時(shí)狀態(tài),CreateMemento()方法用來保存Originator對象的一個(gè)狀態(tài)作為備忘,在以后需要的時(shí)候可以顯示。也就是說通過這個(gè)方法就可以給Originator對象產(chǎn)生一個(gè)備忘錄。RestoreMemento()方法用來從保存的備忘錄中恢復(fù)Originator的狀態(tài),將Originator的狀態(tài)設(shè)置為之前的一個(gè)備忘狀態(tài)。
    下面是備忘錄類Memento類的代碼實(shí)現(xiàn)。
        /// <summary>
        /// 備忘錄類
        /// </summary>
        public class Memento
        {
            /// <summary>
            /// 備忘錄所保存的狀態(tài)
            /// </summary>
            private string state;
     
            /// <summary>
            /// 構(gòu)造函數(shù)
            /// </summary>
            /// <param name="state"></param>
            public Memento(string state)
            {
                this.state = state;
            }
     
            public string GetState()
            {
                return this.state;
            }
     
            public void SetState(string state)
            {
                this.state = state;
            }
        }
    因?yàn)镸emento類需要保存Originator對象的狀態(tài),所以Memento具有一個(gè)Originator狀態(tài)一樣的屬性state,這個(gè)state用來保存Originator的一個(gè)狀態(tài)。
    Memento類的管理者是CareTaker類,CareTaker類的代碼如下。
        /// <summary>
        /// 負(fù)責(zé)保存?zhèn)渫浀墓芾韱T
        /// </summary>
        public class CareTaker
        {
            /// <summary>
            /// 備忘錄
            /// </summary>
            private Memento memento;
     
            /// <summary>
            /// 返回所擁有的備忘錄
            /// </summary>
            /// <returns></returns>
            public Memento retrieveMemento()
            {
                return this.memento;
            }
     
            /// <summary>
            /// 保存一個(gè)備忘錄
            /// </summary>
            /// <param name="memento"></param>
            public void SaveMemento(Memento memento)
            {
                this.memento = memento;
            }
        }
    CareTaker類用來管理Memento對象,所以自身擁有一個(gè)Memento對象。并且有一個(gè)設(shè)置Memento對象方法(SaveMemento)和一個(gè)取得Memento的方法(retrieveMemento)。
    備忘錄模式的核心代碼已經(jīng)定義好了,下面通過客戶端代碼來測試一下備忘錄模式。
            private static Originator o = new Originator();
            private static CareTaker c = new CareTaker();
            public static void Test()
            {
                o.SetState("On");
                c.SaveMemento(o.CreateMemento());
                Console.WriteLine("第一次為" + o.GetState());
                o.SetState("Off");
                Console.WriteLine("第二次為" + o.GetState());
                o.RestoreMemento(c.retrieveMemento());
                Console.WriteLine("恢復(fù)到上一次為" + o.GetState());
            }
    在客戶端代碼中,定義了一個(gè)Originator的對象o和CareTaker 的對象c,首先對o設(shè)置了一個(gè)狀態(tài),并且通過c(c是管理備忘錄的)來把o目前的狀態(tài)備忘(保存為備忘錄)。然后繼續(xù)設(shè)置了一下o的狀態(tài),在此時(shí),o想恢復(fù)自己的狀態(tài)到之前,因?yàn)橄牖謴?fù)的狀態(tài)保存在了備忘錄中,所以通過o的RestoreMemento可以將狀態(tài)恢復(fù)為備忘錄所記載的狀態(tài)。
    客戶端的測試代碼演示的是備忘錄模式的意圖,而之前的代碼演示的是備忘錄模式的結(jié)構(gòu)。
     
    1.1.3.模型表示
    備忘錄模式的UML類圖如下所示。
    在備忘錄模式的類模型圖中可以看出來,Originator,Memento和CareTaker三者之間的關(guān)系,Memento用來保存Originator的一個(gè)狀態(tài),Caretaker用來管理Memento。在Originator需要Memento的時(shí)候,通過Caretaker即可得到。在Originator需要備忘一個(gè)狀態(tài)的時(shí)候,通過Caretaker即可備忘。
    1.1.4.模式分析
    備忘錄模式就是用來在對象的外部可以保存對象的一個(gè)內(nèi)部狀態(tài)。用來存儲另外一個(gè)對象內(nèi)部狀態(tài)的快照。備忘錄即對象的一個(gè)檢查點(diǎn),也是一個(gè)還原點(diǎn)。
    檢查點(diǎn):某一個(gè)快照所處的位置(即一個(gè)狀態(tài))。
    備忘錄模式的意圖是在不破壞封裝性的前提下,捕獲一個(gè)對象的內(nèi)部狀態(tài),并在該對象之外保存這個(gè)狀態(tài),這樣以后就可以將該對象恢復(fù)到原先保存的狀態(tài)。
    通過代碼實(shí)現(xiàn)中的客戶端測試代碼即可看到備忘錄模式的使用場景:
    1、必須保存一個(gè)對象在某一個(gè)時(shí)刻的狀態(tài),這樣以后需要的時(shí)候就能恢復(fù)到之前的狀態(tài)。
    2、如果用接口來讓其他對象直接得到這些狀態(tài),將會暴露對象的實(shí)現(xiàn)細(xì)節(jié)并破壞對象的封裝性。
     
    通過使用備忘錄模式,可以達(dá)到一些效果:
    1、保持封裝邊界。應(yīng)用使用備忘錄模式,將Originator對象中的一些細(xì)節(jié)隔離到了Caretaker和Memento中。保持了Originator類內(nèi)部信息對其他對象的隱藏,從而保持了封裝邊界。
    2、簡化了Originator類,將Originator類中的一些操作交給了Caretaker和Memento。
    3、增加了額外的代價(jià),使用了備忘錄模式,就導(dǎo)致了Originator類中的狀態(tài)在產(chǎn)生備忘錄的時(shí)候復(fù)制了信息,這樣如果需要備忘的信息量很大,那么備忘錄模式會導(dǎo)致額外大量的開銷。
    4、定義窄接口和寬接口。一些語言中可能難以保證只有Originator可訪問備忘錄的狀態(tài)。也就是說Originator之外的對象可能會訪問Memento的狀態(tài),導(dǎo)致不安全。
    5、維護(hù)備忘錄存在潛在的代價(jià)。Caretaker在維護(hù)Memento的時(shí)候,因?yàn)镃aretaker不知道備忘錄Memento所保存的狀態(tài)數(shù)量,所以在通過Caretaker來保存Memento的時(shí)候,可能會導(dǎo)致大量的存儲開銷。
     
    因?yàn)樾Ч?所說的潛在的安全問題,備忘錄還有另外一種安全的實(shí)現(xiàn)方式,也就是將Memento定義為Originator的內(nèi)部類,這樣的話Memento也就是只有Originator才能訪問了。
    安全模式的一種備忘錄模式代碼實(shí)現(xiàn),如下:
        public interface IMementoSafeMode
        {
        }
    定義一個(gè)備忘錄的接口,這樣就封裝了備忘錄的訪問,外部的類都通過訪問接口來訪問備忘錄。備忘錄模式的一個(gè)要點(diǎn)就是不破壞封裝性。
    下面是一種安全實(shí)現(xiàn)的備忘錄管理員類的實(shí)現(xiàn)。
        public class CareTakerSafeMode
        {
            private IMementoSafeMode memento;
            public IMementoSafeMode RetriveMemento()
            {
                return this.memento;
            }
            public void SaveMemento(IMementoSafeMode memento)
            {
                this.memento = memento;
            }
        }
    通過接口的實(shí)現(xiàn),管理員完全不會訪問備忘錄類的,這樣就使得備忘錄類只會暴露給和他有關(guān)系的類,這也是單一原則的體現(xiàn),即迪米特法則(LOD):只與你的朋友通信,不和陌生人說話。
    下面是安全的備忘錄模式的核心實(shí)現(xiàn)。
        public class OriginatorSafeMode
        {
            private string state;
            public OriginatorSafeMode() { }
     
            public IMementoSafeMode CreateMemento()
            {
                 MementoSafeMode mbox = new MementoSafeMode(this.state);
                 return mbox;
            }
            public void RestoreMemento(IMementoSafeMode memento)
            {
                MementoSafeMode mementoSafeMode = (MementoSafeMode)memento;
                this.state = mementoSafeMode.State;
            }
            public string State
            {
                get { return this.state; }
                set { this.state = value; }
            }
     
            protected class MementoSafeMode : IMementoSafeMode
            {
                private string state;
                /// <summary>
                /// </summary>
                /// <param name="state"></param>
                public MementoSafeMode(string state)
                {
                    this.state = state;
                }
                public string State
                {
                    get { return this.state; }
                    set { this.state = value; }
                }
            }
        }
    在之前的備忘錄模式中,備忘錄模式Memento和Originator類是分開的,這里將Memento作為Originator的內(nèi)部類,這樣限制了Memento的訪問范圍,即只有Originator類的內(nèi)部才能訪問。
    注意C#和JAVA在處理內(nèi)部類的時(shí)候有不一致的地方。C#中內(nèi)部類的Public屬性只能是外部類范圍內(nèi)訪問,如果是private則外部類也無法訪問,只有在內(nèi)部類之內(nèi)才能訪問。但是在java中要實(shí)現(xiàn)這樣的效果,內(nèi)部類的所有屬性都必須是Private,這樣才限制了只有外部類訪問。在java中,外部類類似于一個(gè)命名空間,通過外部類可以看到內(nèi)部類(OriginatorSafeMode.MementoSafeMode這樣的寫法就導(dǎo)致了內(nèi)部類的暴露,只不過無法實(shí)例化而已)。即使不能實(shí)例化,但是也對外暴露了內(nèi)部類。在C#中這一點(diǎn)有所改進(jìn),內(nèi)部類在外面根本無法看到。
    實(shí)現(xiàn)了內(nèi)部類的UML類圖省略,內(nèi)部類在UML中使用圈里面包含的一個(gè)叉來顯示,另一端是箭頭,箭頭指向的是內(nèi)部類。Visual Studio 2010中自帶的UML類圖和Visio中的UML類圖都不支持內(nèi)部類的表示,使用rose可以畫。
    1.1.5.思想
    目的:在不破壞封裝性的前提下,捕獲一個(gè)對象的內(nèi)部狀態(tài),并在該對象之外保存這個(gè)狀態(tài)。這樣以后就可以將該對象恢復(fù)到原先保存的狀態(tài)。
    實(shí)現(xiàn)思想:用一個(gè)Memento的類來保存需要恢復(fù)的狀態(tài),可以是一個(gè)或者多個(gè)。并且由另一個(gè)類CareTaker來負(fù)責(zé)恢復(fù)。
    1.1.6.應(yīng)用
    應(yīng)用中如果有類似歷史記錄機(jī)制的功能,那么這個(gè)功能有可能就能使用備忘錄模式,因?yàn)闅v史記錄就是一個(gè)過去的狀態(tài),那么如果要訪問歷史記錄就表示要恢復(fù)狀態(tài)。保存歷史記錄的類就是一個(gè)備忘錄類。
    當(dāng)然了,如果理解了之前的備忘錄模式的實(shí)現(xiàn),那么根據(jù)其要點(diǎn)“恢復(fù)到原先保存的狀態(tài)”,那么如果需要恢復(fù)一個(gè)類的狀態(tài)到以前的某個(gè)狀態(tài),則備忘錄基本上就可以搞定,但是,切忌殺雞用牛刀的悲劇。


    -----------------------------------------------------
    Silence, the way to avoid many problems;
    Smile, the way to solve many problems;

    posted on 2012-11-06 09:45 Chan Chen 閱讀(190) 評論(0)  編輯  收藏 所屬分類: Design Pattern

    主站蜘蛛池模板: 免费视频精品一区二区三区| 在线a免费观看最新网站| 午夜视频免费在线观看| 国产精品色拉拉免费看| 亚洲?V乱码久久精品蜜桃 | 亚洲精品你懂的在线观看| 亚洲综合久久成人69| 看免费毛片天天看| 57pao一国产成视频永久免费| 国产精品免费_区二区三区观看 | 国产亚洲无线码一区二区| 久久夜色精品国产噜噜亚洲a| 国产免费内射又粗又爽密桃视频 | 成年免费a级毛片| ww在线观视频免费观看| 国产精品亚洲二区在线观看 | 亚洲精品在线免费观看视频| 无码欧精品亚洲日韩一区夜夜嗨 | 午夜免费福利在线观看| 久久精品国产亚洲av麻豆| 亚洲aⅴ无码专区在线观看| 热re99久久6国产精品免费| 成人亚洲网站www在线观看| 亚洲国产一区在线观看| a级片免费在线播放| 国产精品嫩草影院免费| 亚洲精品美女在线观看播放| GOGOGO高清免费看韩国| 无码国模国产在线观看免费| 亚洲精品白色在线发布| a级午夜毛片免费一区二区| 免费a级毛片永久免费| 亚洲人成激情在线播放| 无码A级毛片免费视频内谢| 亚洲人成国产精品无码| 亚洲色大18成人网站WWW在线播放| 日韩精品无码专区免费播放| 国产黄色一级毛片亚洲黄片大全| 亚洲欧美成人综合久久久 | 久久精品毛片免费观看| 在线观看亚洲精品福利片|