zoukankan      html  css  js  c++  java
  • Head First设计模式之备忘录模式

    一、定义

    不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样就可以将该对象恢复到原先保存的状态

    二、结构

      备忘录模式中主要有三类角色:

    • 发起人角色:记录当前时刻的内部状态,负责创建和恢复备忘录数据。
    • 备忘录角色:负责存储发起人对象的内部状态,在进行恢复时提供给发起人需要的状态。
    • 管理者角色:负责保存备忘录对象,但是不能对备忘录对象的内容进行操作或检查。

    三、适用场景

    1、需要保存/恢复数据的相关状态场景。

    2、提供一个可回滚的操作。

    四、优缺点

    优点: 

    1、给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。

    2、实现了信息的封装,使得用户不需要关心状态的保存细节。

    缺点:

    消耗资源。

    1、如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。

    2、由于备份的信息是由发起人自己提供的,所以管理者无法预知备份的信息的大小,存在一定的未知风险。

    五、实现

    下面以备份手机通讯录为例子来实现了备忘录模式,具体的实现代码如下所示:

    单次备份

    // 联系人
        public class ContactPerson
        {
            public string Name { get; set; }
            public string MobileNum { get; set; }
        }
    
        // 发起人
        public class MobileOwner
        {
            // 发起人需要保存的内部状态
            public List<ContactPerson> ContactPersons { get; set; }
    
            public MobileOwner(List<ContactPerson> persons)
            {
                ContactPersons = persons;
            }
    
            // 创建备忘录,将当期要保存的联系人列表导入到备忘录中 
            public ContactMemento CreateMemento()
            {
                // 这里也应该传递深拷贝,new List方式传递的是浅拷贝,
                // 因为ContactPerson类中都是string类型,所以这里new list方式对ContactPerson对象执行了深拷贝
                // 如果ContactPerson包括非string的引用类型就会有问题,所以这里也应该用序列化传递深拷贝
                return new ContactMemento(new List<ContactPerson>(this.ContactPersons));
            }
    
            // 将备忘录中的数据备份导入到联系人列表中
            public void RestoreMemento(ContactMemento memento)
            {
                // 下面这种方式是错误的,因为这样传递的是引用,
                // 则删除一次可以恢复,但恢复之后再删除的话就恢复不了.
                // 所以应该传递contactPersonBack的深拷贝,深拷贝可以使用序列化来完成
                this.ContactPersons = memento.contactPersonBack;
            }
    
            public void Show()
            {
                Console.WriteLine("联系人列表中有{0}个人,他们是:", ContactPersons.Count);
                foreach (ContactPerson p in ContactPersons)
                {
                    Console.WriteLine("姓名: {0} 号码为: {1}", p.Name, p.MobileNum);
                }
            }
        }
    
        // 备忘录
        public class ContactMemento
        {
            // 保存发起人的内部状态
            public List<ContactPerson> contactPersonBack;
    
            public ContactMemento(List<ContactPerson> persons)
            {
                contactPersonBack = persons;
            }
        }
    
        // 管理角色
        public class Caretaker
        {
            public ContactMemento ContactM { get; set; }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                List<ContactPerson> persons = new List<ContactPerson>()
                {
                    new ContactPerson() { Name= "Learning Hard", MobileNum = "123445"},
                    new ContactPerson() { Name = "Tony", MobileNum = "234565"},
                    new ContactPerson() { Name = "Jock", MobileNum = "231455"}
                };
                MobileOwner mobileOwner = new MobileOwner(persons);
                mobileOwner.Show();
    
                // 创建备忘录并保存备忘录对象
                Caretaker caretaker = new Caretaker();
                caretaker.ContactM = mobileOwner.CreateMemento();
    
                // 更改发起人联系人列表
                Console.WriteLine("----移除最后一个联系人--------");
                mobileOwner.ContactPersons.RemoveAt(2);
                mobileOwner.Show();
    
                // 恢复到原始状态
                Console.WriteLine("-------恢复联系人列表------");
                mobileOwner.RestoreMemento(caretaker.ContactM);
                mobileOwner.Show();
    
                Console.Read();
            }
        }

    多次备份

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace DesignPatterns.Mememto
    {
        // 联系人
        public class ContactPerson
        {
            public string Name { get; set; }
            public string MobileNum { get; set; }
        }
    
        // 发起人
        public class MobileOwner
        {
            public List<ContactPerson> ContactPersons { get; set; }
            public MobileOwner(List<ContactPerson> persons)
            {
                ContactPersons = persons;
            }
    
            // 创建备忘录,将当期要保存的联系人列表导入到备忘录中 
            public ContactMemento CreateMemento()
            {
                // 这里也应该传递深拷贝,new List方式传递的是浅拷贝,
                // 因为ContactPerson类中都是string类型,所以这里new list方式对ContactPerson对象执行了深拷贝
                // 如果ContactPerson包括非string的引用类型就会有问题,所以这里也应该用序列化传递深拷贝
                return new ContactMemento(new List<ContactPerson>(this.ContactPersons));
            }
    
            // 将备忘录中的数据备份导入到联系人列表中
            public void RestoreMemento(ContactMemento memento)
            {
                if (memento != null)
                {
                    // 下面这种方式是错误的,因为这样传递的是引用,
                    // 则删除一次可以恢复,但恢复之后再删除的话就恢复不了.
                    // 所以应该传递contactPersonBack的深拷贝,深拷贝可以使用序列化来完成
                    this.ContactPersons = memento.ContactPersonBack;
                }
            }
            public void Show()
            {
                Console.WriteLine("联系人列表中有{0}个人,他们是:", ContactPersons.Count);
                foreach (ContactPerson p in ContactPersons)
                {
                    Console.WriteLine("姓名: {0} 号码为: {1}", p.Name, p.MobileNum);
                }
            }
        }
    
        // 备忘录
        public class ContactMemento
        {
            public List<ContactPerson> ContactPersonBack { get; set; }
            public ContactMemento(List<ContactPerson> persons)
            {
                ContactPersonBack = persons;
            }
        }
    
        // 管理角色
        public class Caretaker
        {
            // 使用多个备忘录来存储多个备份点
            public Dictionary<string, ContactMemento> ContactMementoDic { get; set; }
            public Caretaker()
            {
                ContactMementoDic = new Dictionary<string, ContactMemento>();
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                List<ContactPerson> persons = new List<ContactPerson>()
                {
                    new ContactPerson() { Name= "Learning Hard", MobileNum = "123445"},
                    new ContactPerson() { Name = "Tony", MobileNum = "234565"},
                    new ContactPerson() { Name = "Jock", MobileNum = "231455"}
                };
    
                MobileOwner mobileOwner = new MobileOwner(persons);
                mobileOwner.Show();
    
                // 创建备忘录并保存备忘录对象
                Caretaker caretaker = new Caretaker();
                caretaker.ContactMementoDic.Add(DateTime.Now.ToString(), mobileOwner.CreateMemento());
    
                // 更改发起人联系人列表
                Console.WriteLine("----移除最后一个联系人--------");
                mobileOwner.ContactPersons.RemoveAt(2);
                mobileOwner.Show();
    
                // 创建第二个备份
                Thread.Sleep(1000);
                caretaker.ContactMementoDic.Add(DateTime.Now.ToString(), mobileOwner.CreateMemento());
    
                // 恢复到原始状态
                Console.WriteLine("-------恢复联系人列表,请从以下列表选择恢复的日期------");
                var keyCollection = caretaker.ContactMementoDic.Keys;
                foreach (string k in keyCollection)
                {
                    Console.WriteLine("Key = {0}", k);
                }
                while (true)
                {
                    Console.Write("请输入数字,按窗口的关闭键退出:");
    
                    int index = -1;
                    try
                    {
                        index = Int32.Parse(Console.ReadLine());
                    }
                    catch
                    {
                        Console.WriteLine("输入的格式错误");
                        continue;
                    }
    
                    ContactMemento contactMentor = null;
                    if (index < keyCollection.Count && caretaker.ContactMementoDic.TryGetValue(keyCollection.ElementAt(index), out contactMentor))
                    {
                        mobileOwner.RestoreMemento(contactMentor);
                        mobileOwner.Show();
                    }
                    else
                    {
                        Console.WriteLine("输入的索引大于集合长度!");
                    }
                }
            }
        }
    }

    参考

    http://www.cnblogs.com/JsonShare/p/7283972.html

    http://www.runoob.com/design-pattern/memento-pattern.html

    http://www.cnblogs.com/zhili/p/MementoPattern.html


    欢迎阅读本系列文章:Head First设计模式之目录 

  • 相关阅读:
    mysql 实战 or、in与union all 的查询效率
    转:手机流畅的决定性因素
    合批只是对CPU的优化,与GPU没有任何关系
    UNITY 打包时提示sdk tools 或 sdk build tools版本低时可以直接点update 按钮进行更新
    RGB ECT 4BIT 压缩后质量远高于RGB ETC2 4BIT
    Adreno GPU Profiler
    UNITY2018.3 在editor下运行时new memoryprofiler显示 shader占用内存很大的问题在安卓上并没有看到
    VS2017断点调试UNITY2018.3 经常卡住的问题
    一次UNITY闪退问题的定位心得
    UNITY2018 真机开启deepprofiling的操作
  • 原文地址:https://www.cnblogs.com/xcsn/p/7498695.html
Copyright © 2011-2022 走看看