前言
当我们使用程序的时候,经常会出校误操作的选项,这个时候我们又希望回滚这个操作,也就是 “撤销” ,可以通过事务的方式每次记录之前的操作,也可以使用我们现在介绍的,快照模式。
快照模式的定义
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。
优缺点
-
优点
- 简化了调用的操作:调用方不需要去管理和保存其内部状态,所有信息都交由托管方进行处理
- 保证了内部信息的可靠性: 只有发起人能够访问其状态
- 提供了数据恢复的手段
-
缺点
- 特别吃资源
举个🌰(要不要来一把象棋!)
- 首先我们需要定义一个棋子
/// <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})";
}
}
- 再定义个备忘录
/// <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; }
}
- 构造个托管备忘录的工具
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;
}
}
- 跑个测试看看效果
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 秒