• 打游戏要存进度-备忘录模式


    打游戏要存进度-备忘录模式

    学习自

    《大话设计模式》

    备忘录模式漫谈

    备忘录的这种设计思想是非常常见的,比如说围棋游戏的悔棋,绘图软件的撤销功能等等,都或多或少的使用了备忘录模式来处理对象的状态。

    备忘录(Memento): 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这种状态。这样以后就可以将该对象恢复到原来保存的状态。

    我的理解
    保存好重要数据以备反悔之时使用。

    备忘录模式类图

    图片.png | left | 787x166

    • Originator:是备忘录的创建者
    • Memento: 是备忘录对象
    • Caretaker: 持有备忘录对象

    没有使用备忘录模式的代码

    下面这一段代码是模拟了一下,在玩游戏的时候对角色状态的存档与恢复。

    public class GameRole
    {
        public int Vitality { get; set; }
        public int Attack { get; set; }
        public int Defense { get; set; }
        
        public void StateDisplay()
        {
            Console.WriteLine("角色当前状态");
            Console.WriteLine("体力:{0}", this.Vitality);
            Console.WriteLine("攻击力:{0}", this.Attack);
            Console.WriteLine("防御力:{0}", this.Defense);
            Console.WriteLine();
        }
    
        public void GetInitState()
        {
            this.Vitality = 100;
            this.Attack = 100;
            this.Defense = 100;
        }
    
        public void Fight()
        {
            this.Vitality = 0;
            this.Attack = 0;
            this.Defense = 0;
        }
    }
    
    
    static void Main(string[] args)
    {
        GameRole gr = new GameRole();
        gr.GetInitState();
        gr.StateDisplay();
    
        //保存进度
        //!! 这里暴露了细节
        GameRole grBackup = new GameRole();
        grBackup.Vitality = gr.Vitality;
        grBackup.Attack = gr.Attack;
        grBackup.Defense = gr.Defense;
    
        gr.Fight();
        gr.StateDisplay();
    
        //回复之前的状态
        //!!这里暴露的细节
        gr.Vitality = grBackup.Vitality;
        gr.Attack = grBackup.Attack;
        gr.Defense = grBackup.Defense;
    
        gr.StateDisplay();
        Console.ReadKey();
    }
    
    //输出结果
    角色当前状态
    体力:100
    攻击力:100
    防御力:100
    
    角色当前状态
    体力:0
    攻击力:0
    防御力:0
    
    角色当前状态
    体力:100
    攻击力:100
    防御力:100
    

    上面的代码将所有的细节暴露给了客户端,导致客户端承担了太多的职责(保存状态,恢复状态,进行游戏),而且如果一旦游戏人物的属性修改或者添加了,那么客户端相关的代码也必须修改,这些代码紧紧地耦合在了一起。

    使用了备忘录模式的代码

    首先游戏角色这个类并不一定所有的属性都需要备份/存档,我们只需要把我们关系的数据进行存档即可,为了存档这些数据我们需要封装起来,实现职责的分离。

    public class RoleStateMemento
    {
        public int Vitality { get; set; }
        public int Attack { get; set; }
        public int Defense { get; set; }
    
        public RoleStateMemento(int vitality, int attack, int defense)
        {
            this.Vitality = vitality;
            this.Attack = attack;
            this.Defense = defense;
        }
    }
    

    有了存储状态的 Memento 对象后,我们再来修改一下 GameRole 这个类

    public class GameRole
    {
        public int Vitality { get; set; }
        public int Attack { get; set; }
        public int Defense { get; set; }
    
        public void StateDisplay()
        {
            Console.WriteLine("角色当前状态");
            Console.WriteLine("体力:{0}", this.Vitality);
            Console.WriteLine("攻击力:{0}", this.Attack);
            Console.WriteLine("防御力:{0}", this.Defense);
            Console.WriteLine();
        }
    
        public void GetInitState()
        {
            this.Vitality = 100;
            this.Attack = 100;
            this.Defense = 100;
        }
    
        public void Fight()
        {
            this.Vitality = 0;
            this.Attack = 0;
            this.Defense = 0;
        }
    
        /// <summary>
        /// 存档状态
        /// </summary>
        /// <returns></returns>
        public RoleStateMemento SaveRoleState()
        {
            return new RoleStateMemento(this.Vitality, this.Attack, this.Defense);
        }
    
        /// <summary>
        /// 恢复状态
        /// </summary>
        /// <param name="memento"></param>
        public void RecoveryState(RoleStateMemento memento)
        {
            this.Vitality = memento.Vitality;
            this.Attack = memento.Attack;
            this.Defense = memento.Defense;
        }
    }
    

    上面的代码向较于最初的版本多出了两个方法 SaveRoleStateRecoveryState 用来保存当前的角色状态和恢复角色的状态。

    现在我们还差一个Memento的持有者

    public class RoleStateCaretaker
    {
        public RoleStateMemento RoleStateMemento { get; set; }
    }
    

    接下来我们看看客户端的调用

    static void Main(string[] args)
    {
        GameRole gr = new GameRole();
        gr.GetInitState();
        gr.StateDisplay();
    
        //存档
        RoleStateCaretaker caretaker = new RoleStateCaretaker();
        caretaker.RoleStateMemento = gr.SaveRoleState();
    
        //进行游戏
        gr.Fight();
        gr.StateDisplay();
    
        //恢复状态 
        gr.RecoveryState(caretaker.RoleStateMemento);
        gr.StateDisplay();
    
        Console.ReadKey();
    }
    //输出结果
    角色当前状态
    体力:100
    攻击力:100
    防御力:100
    
    角色当前状态
    体力:0
    攻击力:0
    防御力:0
    
    角色当前状态
    体力:100
    攻击力:100
    防御力:100
    

    现在客户端已经无法观察到保存状态和恢复状态的细节了,所有的细节都被封装到了类中,现在如果对保存/恢复状态的业务进行修改,也不会影响到客户端的代码。

    备忘录模式的弊端

    如果备忘录模式需要存储的状态数据非常多的话,那么就会非常消耗内存。

  • 相关阅读:
    最小生成树 kruskal算法&prim算法
    Floyd算法解决多源最短路问题
    dijkstra算法解决单源最短路问题
    改进后的作业排序
    第一篇 基本结构
    循环轮转算法
    在线工具生成接入信息mqtt.fx快速接入阿里云
    NodeMCU使用ArduinoJson判断指定键值对存在与否
    NodeMCU获取并解析心知天气信息
    快速导出jekyll博客文件进行上传部署
  • 原文地址:https://www.cnblogs.com/slyfox/p/9296938.html
Copyright © 2020-2023  润新知