设计模式专题(十三) ——备忘录模式
设计模式专题(十三)——备忘录模式
(原创内容,转载请注明来源,谢谢)
一、概述
备忘录模式(Memento)是在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样就可以将该对象恢复到原先保存的状态。
备忘录模式将保存的细节封装在备忘录中,当程序变动需要改动保存细节,也不需要客户端进行改动。该模式适合场景复杂,但是需要维护或记录属性历史的类。并且,通常不需要全量保存,可以通过保存的部分数据恢复整个细节。
另外,备忘录模式可以把保存的数据进行加密,则保证保存的数据完整性。
备忘录模式使用场景较多,如文本编辑的撤销、棋类游戏的悔棋、单机游戏的存档等,都会用到备忘录模式。
二、类图
三、具体设计
备忘录模式中有三个角色。
1、发起人Originator,是被保存的类,其可以自定义方式让用户进行保存。另外,需要提供解析已经保存的数据,实现恢复数据的功能。
2、备忘录Memento,用于生成originator以某种方式存储后的数据。
3、管理者Caretaker,其不可以操作或者查看memento的细节,仅用于管理当前保存的各种备忘录,供客户端调用。
四、实现方式
用PHP实现备忘录模式,可以用到php的内部魔术方法__sleep()和__wakeup。当保存类的时候,通常会用serialize,则__sleep()可以控制序列化哪些内容。
php实现上述三种角色,方式如下:
1、Originator,类的状态,提供当前状态、选择性保存、解析保存的状态。需要使用__sleep()和__wakeup;但也可以自定义保存方式,如果要自定义保存,则需要把__sleep()和__wakeup的返回值都设置成null,避免外界使用serialize来进行保存。
2、Memento,主要是类存储后的加密与解密,为了保证存储的数据不被外界改动,在经过备忘录的时候,进行加密和解密,保证数据的安全性。另外,还有一个很重要的功能,就是获取类存储的功能,供管理者类调用。
3、Caretaker,根据客户端的要求,调用Memento获取到originator的存储以及加密后的内容,并存储在本地文件或数据库等地方;根据客户端的恢复要求,去本地文件或数据库中查找数据,并调用memento获取解密和解析后的结果,返回给客户端。
五、程序实现(关键内容)
class Originator{
private$piecePosArr;//三维数组,第一维是第几步,第二维是每个棋子和其位置的map,第三维是棋子的位置
publicfunction __construct(){
$piecePosArr= array(
'step1'=> array(array('piece1' => 'pos1'),array('piece2' => 'pos2')...),
'step2'=> array(array('piece1' => 'pos1'),array('piece2' => 'pos2')...),
//......
);
}
publicfunction __sleep(){
$pieces= $this->piecePosArr;
if(!empty($pieces)){
foreach($piecesas &$piece){
//....进行相应操作
}
}
return$pieces;
}
publicfunction __wakeup(){
}
}
//Memento
class Memento{
private$orig;
privateconst $secret = array(
'a','b', 'c', 'd', 'e', 'f', 'g', //...到z
);
privateconst CODE = 'my_memento';
privatefunction getEnCode($len){
//对服务端和客户端存储的内容分别加密,
//并把服务端的内容存储在服务器(作为钥匙),把客户端的内容保存在客户端
//这样如果客户端私自篡改数据,服务端的数据也会无法解析客户端的数据,即保证数据的安全
returnarray(
'client'=> 'toclient',
'service'=> 'toservice'
);
}
//加密
publicfunction saveData(Originator $orig, $userId){
$this->orig= $orig;
//加密,在数组中加入内容,加密的内容存储在本地进行校验
$arrSecret= $this->getEnCode(10);
//存储在服务端的redis,省略redis连接部分
$redis->set("chees:$userId:key",$arrSecret['service']);
return$arrSecret['client'];
}
//解析
publicfunction recoverData($encryptData, $clientCode){
}
}
//caretaker
class CareTaker{
publicfunction saveData(Originator $orig, $userId){
$memento= new Memento();
$res= $memento->saveData($orig, $userId);
//....获取的结果存在客户端的本地文件,根据userid->值的方式进行存储
}
publicfunction recoverData($userId){
//.....根据userid从用户本地取得data的内容
$memento= new Memento();
$res= $memento->recoverData($data, $userId);
}
}
上述程序仅实现关键部分,备忘录在特定应用场景下非常实用。
——written by linhxx 2017.08.10
相关阅读:
设计模式专题(十二)——状态模式
设计模式专题(十一)——抽象工厂模式
设计模式专题(十)——观察者模式
设计模式专题(九) ——外观模式
设计模式专题(八) ——模板方法模式
设计模式专题(七)——建造者模式
设计模式专题(六)——原型模式
设计模式专题(五)——工厂方法模式
设计模式专题(四)——代理模式
设计模式专题(三)——装饰模式
设计模式专题(二)——策略模式
设计模式专题(一)——面向对象的设计原则
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 如何将elastic search 的健康状态由红色red变为绿色green
- 拥抱K8s系列-01
- elastic search 修改日志级别为warn
- prometheus学习笔记(3)-使用exporter监控mysql
- elasticsearch query里面的slop选项
- 彻底明白Android设计模式—(动态)代理模式
- elastic search 如何将yellow 状态变为green健康状态
- All clients has disconnected from. You can graceful shutdown now., dubbo version: , current host
- Failed to instantiate org.mybatis.spring.SqlSessionTemplate Constructor threw exception
- 护网之Linux应急处理操作手册
- 对新版安全狗学习
- redis key的删除策略及LRU的实现
- 护网Linux应急处置操作手册-Tools篇
- 加密,编码三问
- 我是怎么挖掘yii2反序列化0day的