设计模式专题(十三) ——备忘录模式

设计模式专题(十三)——备忘录模式

(原创内容,转载请注明来源,谢谢)

一、概述

备忘录模式(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

相关阅读:

设计模式专题(十二)——状态模式

设计模式专题(十一)——抽象工厂模式

设计模式专题(十)——观察者模式

设计模式专题(九) ——外观模式

设计模式专题(八) ——模板方法模式

设计模式专题(七)——建造者模式

设计模式专题(六)——原型模式

设计模式专题(五)——工厂方法模式

设计模式专题(四)——代理模式

设计模式专题(三)——装饰模式

设计模式专题(二)——策略模式

设计模式专题(一)——面向对象的设计原则

原文发布于微信公众号 - 决胜机器学习(phpthinker)

原文发表时间:2017-08-10

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏jessetalks

bootstrap + requireJS+ director+ knockout + web API = 一个时髦的单页程序

也许单页程序(Single Page Application)并不是什么时髦的玩意,像Gmail在很早之前就已经在使用这种模式。通常的说法是它通过避免页面刷新...

3005
来自专栏社区的朋友们

TAF 必修课(四):过载保护

经过实习过程中,leader和导师在思维逻辑上的指导,自己再有意识的加以训练,我觉得非常受益。就如这部分的理解,目前就加深了很多。所以说,思维决定行为、行为决定...

7220
来自专栏小鄧子的技术博客专栏

About ExecutorService(3),我所认识的AsyncTask

打开电脑的时候已经深夜十二点多了,周末两天过的实在憋屈,小伙伴喊我去打球,因为脚趾的伤至少还要数周才能痊愈,于是当了一天的啦啦队,第二天果断没再去。。。

1093
来自专栏葡萄城控件技术团队

Winform文件下载之WebClient

最近升级了公司内部使用的一个下载小工具,主要提升了下面几点: 1. 在一些分公司的局域网中,连接不上外网 2. 服务器上的文件更新后,下载到的还是更新前的文件 ...

1985
来自专栏樊华恒的专栏

海量之道系列文章之弱联网优化 (四)

我们需要有一条(相对)快速、(相对)顺畅、(相对)稳定的网络通道承载业务数据的传输,这条路的最好是传输快、不拥堵、带宽大、收费少。如何才能做到快链路,且听下面分...

7000
来自专栏SDNLAB

【每日播报】OpenDaylight与Mininet应用实战之复杂网络验证(五)

1 多交换机的测试 Mininet中本身就支持多交换机网络拓扑的模拟创建,可通过Python API自定义拓扑创建满足使用者在仿真过程中的多方位需求。 下面举出...

3548
来自专栏有趣的Python

Scrapy分布式爬虫打造搜索引擎-(五)爬虫与反爬虫的战争Python分布式爬虫打造搜索引擎

Python分布式爬虫打造搜索引擎 基于Scrapy、Redis、elasticsearch和django打造一个完整的搜索引擎网站 五、爬虫与反爬虫 1. 基...

4484
来自专栏非典型技术宅

Swift实践:使用CoreData完成一个通讯录存储

1534
来自专栏极客生活

macOS扫雷逆向破解

其中安全帽只有10个,用完了之后就需要在App Store进行购买,同时「高级」和「自定义」功能也需要在应用商店进行购买才可以玩。

1022
来自专栏老付的网络博客

并发问题

在编程的时候我们经常会碰到并发的问题,如果处理不好很有可能造成业务数据的错误。我们思考,到底什么是并发问题? 简单的来说我们可以把并发问题归纳为:未写入而先读取...

775

扫码关注云+社区