• 设计模式20:Memento 备忘录模式(行为型模式)


    Memento 备忘录模式(行为型模式)

    对象状态的回溯

    对象状态的变化无端,如何回溯、恢复对象在某个点的状态?

    动机(Motivation)

    在软件构建过程中,某些对象的状态在转换过程中,可能由于某种需要,要求程序能够回溯到对象之前处于某个点时的状态。如果使用一些共有接口来让其他对象得到对象的状态,便会暴露对象的细节实现。

    如何实现对象状态良好保存与恢复?同时又不会因而破坏对象本身的封装性。

    意图(Intent)

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

    示例代码

        public class Rectangle:ICloneable
        {
            private int x;
            private int y;
            private int width;
            private int height;
    
            public Rectangle(int x,int y,int width,int height)
            {
                this.x = x;
                this.y = y;
                this.width = width;
                this.height = height;
            }
    
            public void MoveTo(Point p)
            {
                //...
            }
    
            public void ChangeWidth(int width)
            {
                //...
            }
    
            public void ChangeHeight(int height)
            {
                //...
            }
    
            public void Draw(Graphic graphic)
            {
                //...
            }
    
            public object Clone()
            {
                return this.MemberwiseClone();
            }
        }
    
        class GraphicSystem
        {
            //原发器对象
            //有必要对自身的状态进行保存,然后在某个点处又需要恢复内部状态
            Rectangle r=new Rectangle(0,0,10,10);
    
            //备忘录对象
            //保存原发器对象内部状态,但是不提供原发器对象支持的操作
            Rectangle rSaved=new Rectangle(0,0,10,10);//不符合备忘录对象的要求
    
            public void Process()
            {
                rSaved = r.Clone() as Rectangle;
            }
        }

     由于rSaved不应该提供原发器对象支持的操作,所以它不符合备忘录对象的要求。不符合单一职责原则。

    解决方案一:

        public class Rectangle
        {
            private int x;
            private int y;
            private int width;
            private int height;
    
            public Rectangle(int x,int y,int width,int height)
            {
                this.x = x;
                this.y = y;
                this.width = width;
                this.height = height;
            }
    
            public void MoveTo(Point p)
            {
                //...
            }
    
            public void ChangeWidth(int width)
            {
                //...
            }
    
            public void ChangeHeight(int height)
            {
                //...
            }
    
            public void Draw(Graphic graphic)
            {
                //...
            }
    
            public RectangleMemento CreateMemento()
            {
                RectangleMemento rm=new RectangleMemento();
                rm.SetState(this.x,this.y,this.width,this.height);
                return rm;
            }
    
            public void SetMento(RectangleMemento rm)
            {
                this.x = rm.x;
                this.y = rm.y;
                this.width = rm.width;
                this.height = rm.height;
            }
        }
    
        public class RectangleMemento
        {
            internal int x;
            internal int y;
            internal int width;
            internal int height;
    
            internal void SetState(int x, int y, int width, int height)
            {
                this.x = x;
                this.y = y;
                this.width = width;
                this.height = height;
            }
        }
    
        class GraphicSystem
        {
            //原发器对象
            //有必要对自身的状态进行保存,然后在某个点处又需要恢复内部状态
            Rectangle r=new Rectangle(0,0,10,10);
    
            //备忘录对象
            //保存原发器对象内部状态,但是不提供原发器对象支持的操作
            RectangleMemento rSaved = new RectangleMemento();
    
            public void Process()
            {
                rSaved = r.CreateMemento();
            }
        }

     解决方案二:通过序列化方式

       [Serializable]
      public class Rectangle { private int x; private int y; private int width; private int height; public Rectangle(int x,int y,int width,int height) { this.x = x; this.y = y; this.width = width; this.height = height; } public void MoveTo(Point p) { //... } public void ChangeWidth(int width) { //... } public void ChangeHeight(int height) { //... } public void Draw(Graphic graphic) { //... } }    //通用备忘录类 class GeneralMenento { MemoryStream rSaved = new MemoryStream(); internal void SetState(object obj) { BinaryFormatter bf = new BinaryFormatter(); bf.Serialize(rSaved, obj); } internal object GetState() { BinaryFormatter bf = new BinaryFormatter(); rSaved.Seek(0, SeekOrigin.Begin); return bf.Deserialize(rSaved); } } class GraphicSystem { //原发器对象 //有必要对自身的状态进行保存,然后在某个点处又需要恢复内部状态 Rectangle r=new Rectangle(0,0,10,10); GeneralMenento rSaved = new GeneralMenento(); public void Process() { rSaved.SetState(r); } }

    结构(Structure)

    Memento模式的几个要点

    • 备忘录(Memento)存储原发器(Originator)对象内部状态,在需要时恢复原发器状态。Memento模式适用于“由原发器管理,却又必须存储在原发器之外的信息”。
    • 在实现Memento模式中,要防止原发器以外的对象访问备忘录对象。备忘录对象由两个接口,一个为原发器使用的宽接口,一个为其他对象使用的窄接口。
    • 在实现Memento模式时,要考虑拷贝对象状态的效率问题,如果对象开销较大,可以采用某种增量式改变来改进Memento模式。

    转载请注明出处:

    作者:JesseLZJ
    出处:http://jesselzj.cnblogs.com

  • 相关阅读:
    jQuery serialize()方法无法获取表单数据
    gorm 零值不更新问题
    SpringCloud Nacos使用和配置,SpringCloud Nacos 服务注册中心配置使用
    Nacos longPolling error,Nacos1.4.1服务配置文件更新一次后报错
    Windows Mysql5.7安装和配置,Windows 安装多个Mysql
    SpringCloud Gateway使用和配置,SpringCloud Gateway predicates详细配置
    Windows RabbitMQ_3.8 安装和配置,Windows erlang下载
    SpringCloud Hystrix dashboard2.2.7使用和配置,SpringCloud Hystrix dashboard服务监控
    SpringCloud Hystrix使用和配置,SpringCloud Hystrix服务熔断降级
    SpringCloud OpenFeign使用和配置,Java OpenFeign 使用教程
  • 原文地址:https://www.cnblogs.com/jesselzj/p/4774641.html
Copyright © 2020-2023  润新知