前言

当我们使用程序的时候,经常会出校误操作的选项,这个时候我们又希望回滚这个操作,也就是 “撤销” ,可以通过事务的方式每次记录之前的操作,也可以使用我们现在介绍的,快照模式。

快照模式的定义

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。

优缺点

  • 优点

    1. 简化了调用的操作:调用方不需要去管理和保存其内部状态,所有信息都交由托管方进行处理
    2. 保证了内部信息的可靠性: 只有发起人能够访问其状态
    3. 提供了数据恢复的手段
  • 缺点

    1. 特别吃资源

举个🌰(要不要来一把象棋!)

  1. 首先我们需要定义一个棋子
    /// <summary>
    /// 调用人
    /// </summary>
    public class Chess
    {
        public Chess() { }
        public Chess(int color, int index_X, int index_Y, string chessName)
        {
            this.Color = color;
            this.Index_X = index_X;
            this.Index_Y = index_Y;
            this.ChessName = chessName;

        }
        public int Color { get; set; }
        public int Index_X { get; set; }
        public int Index_Y { get; set; }
        public string ChessName { get; set; }
        /// <summary>
        /// 创建备忘录
        /// </summary>
        /// <param name="chess"></param>
        /// <returns></returns>
        public ChessMemory CreateMemory(Chess chess)
        {
            //对象克隆的时候要小心哦,如果直接复制,对象改变了也会导致备忘录的内容改变了,
            //所以这里使用了序列化与反序列化做一个深克隆
            var serializeData = JsonSerializer.Serialize(chess);
            return JsonSerializer.Deserialize<ChessMemory>(serializeData);
        }
        /// <summary>
        /// 咋?还不给悔棋了?
        /// </summary>
        /// <param name="chessMemory"></param>
        public void Restore(ChessMemory chessMemory)
        {
            this.ChessName = chessMemory.ChessName;
            this.Index_X = chessMemory.Index_X;
            this.Index_Y = chessMemory.Index_Y;
            this.Color = chessMemory.Color;
        }
        public override string ToString()
        {
            string colorName = this.Color == 1 ? "红方" : "黑方";
            return $"{colorName}{this.ChessName}:落子({this.Index_X},{this.Index_Y})";
        }
    }
  1. 再定义个备忘录
    /// <summary>
    /// 备忘录信息
    /// </summary>
    public class ChessMemory
    {
        public int Color { get; set; }
        public int Index_X { get; set; }
        public int Index_Y { get; set; }
        public string ChessName { get; set; }
        public int Step { get; set; }
    }
  1. 构造个托管备忘录的工具
 public class ChessCaretaker
    {
        private int Step = 0;
        private List<ChessMemory> chessMemorieLinked = new List<ChessMemory>();
        private List<ChessMemory> chessMemorieLinkedBackup = new List<ChessMemory>();
        public void AddMemory(ChessMemory chessMemory)
        {
            chessMemorieLinked.Add(chessMemory);
        }
        public ChessMemory PreviousStep()
        {

            var lastData = chessMemorieLinked.LastOrDefault();
            chessMemorieLinkedBackup.Add(lastData);
            chessMemorieLinked.Remove(lastData);
            return chessMemorieLinked.LastOrDefault();
        }
        public ChessMemory NextStep()
        {
            var lastData = chessMemorieLinkedBackup.LastOrDefault();
            chessMemorieLinked.Add(lastData);
            chessMemorieLinkedBackup.Remove(lastData);
            return lastData;
        }
    }
  1. 跑个测试看看效果
protected readonly ITestOutputHelper Output;

    public UnitTest1(ITestOutputHelper tempOutput)
    {
        Output = tempOutput;
    }
    [Fact]
    public void Test1()
    {
        //使用Fixture构造一个数据填充
        var gamerDataFixTure = new Fixture();
        //准备“备忘录”
        var chessCaretaker = new ChessCaretaker();

        Output.WriteLine("开始下棋");
        var gamerData = new Chess();
        for (int i = 0; i < 10; i++)
        {
            //下一步棋
            gamerData = gamerDataFixTure.Create<Chess>();
            Output.WriteLine($"保存记录:第{i}步");
            Output.WriteLine(gamerData.ToString());
            //添加到备忘录
            chessCaretaker.AddMemory(gamerData.CreateMemory(gamerData));
        }
        Output.WriteLine($"我要开始悔棋了");
        //悔棋
        for (int i = 0; i < 3; i++)
        {
            //获取上一步的数据
            var previousData = chessCaretaker.PreviousStep();
            if (previousData != null)
            {
                //还原上一步的数据
                gamerData.Restore(previousData);
                Output.WriteLine($"上一步");
                Output.WriteLine(gamerData.ToString());
            }
            else
            {
                Output.WriteLine($"老板,已经帮你还原成初始值了!");
            }

        }
        Output.WriteLine($"我要开始还原了");
        //还原
        for (int i = 0; i < 3; i++)
        {
            //获取下一步的数据
            var nextStep = chessCaretaker.NextStep();
            if (nextStep != null)
            {
                //还原下一步的数据
                gamerData.Restore(nextStep);
                Output.WriteLine($"下一步");
                Output.WriteLine(gamerData.ToString());
            }
            else
            {
                Output.WriteLine($"不能下一步了,达咩哦!");

            }

        }
    }
  标准输出消息:
 开始下棋
 保存记录:第0步
 黑方ChessName762d996e-cf26-4baf-a862-aeacbd709237:落子(193,163)
 保存记录:第1步
 黑方ChessNamebdace414-50f7-4a47-8c79-9b9a6947f437:落子(207,186)
 保存记录:第2步
 黑方ChessNameffc67c3a-45a5-4f83-9f6b-174e41652971:落子(137,146)
 保存记录:第3步
 黑方ChessName4914f594-9be7-4597-be7a-a99d4919bb81:落子(59,133)
 保存记录:第4步
 红方ChessName4e62f74d-55be-4d91-8efd-4b7cda01f9ad:落子(249,136)
 保存记录:第5步
 黑方ChessName95e1bcfe-1d39-4675-99a2-feda737982ea:落子(130,242)
 保存记录:第6步
 黑方ChessName3e59ce2e-ff03-470e-9ece-88adc76b6005:落子(23,69)
 保存记录:第7步
 黑方ChessName41f56898-173c-4397-a182-f11616541d14:落子(83,159)
 保存记录:第8步
 黑方ChessName3e99fa1f-29ac-4db5-999b-4051adaae6cf:落子(135,120)
 保存记录:第9步
 黑方ChessNamedc615762-1cdb-40d5-b2dd-7cdf1393421c:落子(17,87)
 我要开始悔棋了
 上一步
 黑方ChessName3e99fa1f-29ac-4db5-999b-4051adaae6cf:落子(135,120)
 上一步
 黑方ChessName41f56898-173c-4397-a182-f11616541d14:落子(83,159)
 上一步
 黑方ChessName3e59ce2e-ff03-470e-9ece-88adc76b6005:落子(23,69)
 我要开始还原了
 下一步
 黑方ChessName41f56898-173c-4397-a182-f11616541d14:落子(83,159)
 下一步
 黑方ChessName3e99fa1f-29ac-4db5-999b-4051adaae6cf:落子(135,120)
 下一步
 黑方ChessNamedc615762-1cdb-40d5-b2dd-7cdf1393421c:落子(17,87)



测试运行成功。
测试总数: 1
     通过数: 1
总时间: 1.0358 秒
上一篇 下一篇