设计模式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

时间: 2024-10-07 08:14:12

设计模式20:Memento 备忘录模式(行为型模式)的相关文章

设计模式(十四):Command命令模式 -- 行为型模式

1.概述 在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计,使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活. 例子1:电视机遥控器 : 遥控器是请求的发送者,电视机是请求的接收者,遥控器上有一些按钮如开,关,换频道等按钮就是具体命令,不同的按钮对应电视机的不同操作. 2.问题 在软件系统中,“行为请求者”与“行为实现者”通常呈现一种“紧

php设计模式(一):简介及创建型模式

我们分三篇文章来总结一下设计模式在PHP中的应用,这是第一篇创建型模式. 一.设计模式简介 首先我们来认识一下什么是设计模式: 设计模式是一套被反复使用.容易被他人理解的.可靠的代码设计经验的总结. 设计模式不是Java的专利,我们用面向对象的方法在PHP里也能很好的使用23种设计模式. 那么我们常说的架构.框架和设计模式有什么关系呢? 架构是一套体系结构,是项目的整体解决方案:框架是可供复用的半成品软件,是具体程序代码.架构一般会涉及到采用什么样的框架来加速和优化某部分问题的解决,而好的框架代

23种设计模式介绍(三)---- 行为型模式

由于设计模式篇幅比较大,如果在一篇文章讲完所有的设计模式的话不利于阅读.于是我把它分为三篇文章 23种设计模式介绍(一)---- 创建型模式 23种设计模式介绍(二)---- 结构型模式 23种设计模式介绍(三)---- 行为型模式 设计模式都是比较抽象的概念,所以大家一定要确保看懂类图,而后再自己写代码加强记忆. 概述 行为型模式一共有11种: 模板方法模式(Template Method) 策略模式(Strategy) 命令模式(Command) 中介者模式(Mediator) 观察者模式(

【读书笔记】设计模式第6章:行为型模式2

本文主要分析了中介者模式.观察者模式.备忘录模式.访问者模式.状态模式.解释器模式,介绍它们的定义.优缺点.使用场景,以及实例代码.为了深刻地理解设计模式,最重要的还是动手编写代码. 我参照书中的例程重新构想了一些更加生动.易于理解的例子,希望大家喜欢. 代码可以通过以下链接进行浏览: http://git.oschina.net/caipeichao/java-design-pattern 这些代码都经过编译运行,保证没有错误. 中介者模式 定义 也叫调停者模式 用一个中介对象来封装一系列同事

设计模式(五) : 创建型模式--建造者模式

建造者模式的意图是将产品的内部表象和产品的生产过程分割开来. 类图: 示意性代码: package com.javadesignpattern.builder; public interface Builder { public void buildPart1(); public void buildPart2(); public Product retrieveProduct(); } ? 1 2 3 4 5 6 7 package com.javadesignpattern.builder;

设计模式(十五):Iterator迭代器模式 -- 行为型模式

1.概述 类中的面向对象编程封装应用逻辑.类,就是实例化的对象,每个单独的对象都有一个特定的身份和状态.单独的对象是一种组织代码的有用方法,但通常你会处理一组对象或者集合. 集合不一定是均一的.图形用户界面框架中的 Window 对象可以收集任意数量的控制对象 - Menu.Slider 和 Button.并且,集合的实现可以有多种方式:PHP 数字是一个集合,但也是一个散列表,一个链接列表,一个堆栈以及队列. 例子1:电视遥控器的频道遍历 2.问题 如何操纵任意的对象集合? 如一个列表(Lis

23种设计模式介绍(一)---- 创建型模式

由于设计模式篇幅比较大,如果在一篇文章讲完所有的设计模式的话不利于阅读.于是我把它分为三篇文章 23种设计模式介绍(一)---- 创建型模式 23种设计模式介绍(二)---- 结构型模式 23种设计模式介绍(三)---- 行为型模式 由于设计模式都是比较抽象的概念,所以大家一定要确保看懂类图,而后再自己写代码加强记忆. 简介 设计模式分为三大类: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接

设计模式(三) : 创建型模式--工厂方法模式

工厂方法模式区别与简单工厂模式主要在于,factory中对对象的实例化延迟到了子类的factory中, 这也是优于简单工厂的地方.下面看这个模式的类图(截自<java与模式>): 示意性代码: ? 1 2 3 4 5 6 7 package com.javadesignpattern.factorymethod; public interface Creator {          public Product fatcory(); } package com.javadesignpatte

设计模式(六):Singleton 单件模式 -- 创建型模式

1.定义 当需要控制一个类的实例数量且调用者可以从一个公共的访问点访问时. 2.适用场景 1. 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时. 2. 当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时. 3.评价 优点: 1. 对唯一实例的受控访问, 因为Singleton类封装它的唯一实例,所以它可以严格的控制客户怎样以及何时访问它. 2. 缩小名空间,Singleton模式是对全局变量的一种改进.它避免了那些存储唯一实例的全局变量污染名空

设计模式(五):PROTOTYPE原型模式 -- 创建型模式

1.定义 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 2.适用场景 原型模式的主要思想是基于现有的对象克隆一个新的对象出来,一般是有对象的内部提供克隆的方法,通过该方法返回一个对象的副本,这种创建对象的方式,相比我们之前说的几类创建型模式还是有区别的,之前的讲述的工厂模式与抽象工厂都是通过工厂封装具体的new操作的过程,返回一个新的对象,有的时候我们通过这样的创建工厂创建对象不值得,特别是以下的几个场景的时候,可能使用原型模式更简单也效率更高. • 1)当一个系统应该独立于