Java设计模式之十二 ---- 备忘录模式和状态模式
前言
在上一篇中我们学习了行为型模式的策略模式(Strategy Pattern)和模板模式(Template Pattern)。本篇则来学习下行为型模式的两个模式,备忘录模式(Memento Pattern)和状态模式(Memento Pattern)。
备忘录模式
简介
备忘录模式(Memento Pattern)用于保存一个对象的某个状态,以便在适当的时候恢复对象,该模式属于行为型模式。
其主要目的是在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
备忘录模式,其主要的的思想就是备份。例如,txt、word等文档的保存,游戏的存档,操作系统的备份,也包括我们经常用的快捷键Ctrl+Z等等。除了以上的这些应用,我们在编程中接触最多的估计就是数据库的事物了,它提供了一种恢复机制,可在出现异常的时候进行还原。
备忘录模式主要由这三个角色组成,备忘录角色(Memento)、发起人角色(Originator)和负责人(Caretaker)角色。
- 备忘录(Memento):主要的功能是包含要被恢复的对象的状态。
- 发起人(Originator):在创建的时候,会在备忘录对象中存储状态。
- 负责人(Caretaker):主要是负责从备忘录对象中恢复对象的状态。
示例图如下:
我们这里依旧用一个示例来进行说明吧。
我们在玩游戏有的时候,会经常用到一个游戏功能,那就是存档。主要是为了保存游戏进度,防止信息丢失,并且也可以通过进行读档恢复保存的信息。比如xuwujing在玩一个游戏的时候,创建好角色之后进行打怪练级,然后挑战BOSS,不过担心挑战失败,于是便在挑战前进行存档,存档成功之后,再来进行挑战BOSS。
那么我们可以根据这个场景来使用备忘录模式来进行开发。
首先定义一个Memento,也就是游戏存档的信息,主要存储游戏人物等级和生命值。
class SaveMsg{
private int level;
private int life;
public SaveMsg( int level, int life) {
super();
this.level = level;
this.life = life;
}
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
public int getLife() {
return life;
}
public void setLife(int life) {
this.life = life;
}
}
然后再定义一个Originator,这里就是玩家了,除了等级和生命值信息外,玩家还可以进行存档、读档,以及进行一些活动,打怪练级和挑战BOSS。
那么代码如下:
class Player {
//等级
private int level;
//生命值
private int life;
public Player( int level, int life) {
super();
this.level = level;
this.life = life;
}
//保存信息
public SaveMsg saveStateToMemento() {
return new SaveMsg(level,life);
}
//恢复信息
public void getStateFromMemento(SaveMsg sm) {
this.level = sm.getLevel();
this.life = sm.getLife();
}
//获取当前状态
public void getStatus() {
System.out.println("玩家xuwujing当前信息:");
System.out.println("人物等级:"+level+",人物生命:"+life);
}
//练级
public void leveling() {
this.level = this.level+1;
this.life = this.life+10;
System.out.println("恭喜玩家xuwujing升级!等级提升了1,生命提升了10!");
}
//挑战BOSS
public boolean challengeBOSS() {
//设置条件
return this.level>2&&this.life>100;
}
}
最后在定义一个Caretaker,作为游戏存档页,用于保存存档信息。
代码如下:
class GameSavePage{
private SaveMsg sm;
public SaveMsg getSm() {
return sm;
}
public void setSm(SaveMsg sm) {
this.sm = sm;
}
}
编写好之后,那么我们来进行测试。
相应的测试代码如下:
public static void main(String[] args) {
int level = 1;
int life = 100;
//创建一个玩家
Player player =new Player(level, life);
System.out.println("玩家xuwujing进入游戏!");
//状态
player.getStatus();
//进行练级
player.leveling();
GameSavePage savePage =new GameSavePage();
//状态
player.getStatus();
System.out.println("玩家xuwujing正在存档...");
//第一次存档
savePage.setSm(player.saveStateToMemento());
System.out.println("玩家xuwujing存档成功!");
System.out.println("玩家xuwujing挑战新手村的BOSS!");
boolean flag=player.challengeBOSS();
if(flag) {
System.out.println("玩家xuwujing挑战BOSS成功!");
return;
}
System.out.println("玩家xuwujing挑战BOSS失败!游戏结束!开始读取存档...");
savePage.getSm();
System.out.println("玩家xuwujing读取存档成功!");
//进行练级
player.leveling();
//状态
player.getStatus();
System.out.println("玩家xuwujing挑战新手村的BOSS!");
flag=player.challengeBOSS();
if(flag) {
System.out.println("玩家xuwujing挑战BOSS成功!");
return;
}
}
输出结果:
玩家xuwujing进入游戏!
玩家xuwujing当前信息:
人物等级:1,人物生命:100
恭喜玩家xuwujing升级!等级提升了1,生命提升了10!
玩家xuwujing当前信息:
人物等级:2,人物生命:110
玩家xuwujing正在存档...
玩家xuwujing存档成功!
玩家xuwujing挑战新手村的BOSS!
玩家xuwujing挑战BOSS失败!游戏结束!开始读取存档...
玩家xuwujing读取存档成功!
恭喜玩家xuwujing升级!等级提升了1,生命提升了10!
玩家xuwujing当前信息:
人物等级:3,人物生命:120
玩家xuwujing挑战新手村的BOSS!
玩家xuwujing挑战BOSS成功!
备忘录模式优点
给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态;
实现了信息的封装,使得用户不需要关心状态的保存细节;
备忘录模式缺点
非常的消耗资源;
客户端必须知道所有的策略类才能进行调用;
使用场景:
需要保存/恢复数据的相关状态场景;
状态模式
简介
状态模式(State Pattern)属于行为型模式,其状态的对象和一个行为随着状态对象改变而改变。
其主要目的解决的是当控制一个对象状态转换的条件表达式过于复杂是的情况。把状态的判断逻辑转移到表示不同状态一系列类中,可以把复杂的判断简单化。
状态模式,其主要的的思想就是提供一种状态,提供给客户端进行调用。状态可谓无处不在,无论是电脑、手机等电子产品的开机和关机的状态,还是经常用到的网络在线和离线状态,即使是在我们编程中Tcp也有创建、监听、关闭状态。
状态模式主要由环境角色(Context)、 抽象状态(State)和具体状态(Concrete State)组成。
环境角色(Context): 它定义了客户程序需要的接口并维护一个具体状态角色的实例,将与状态相关的操作委托给当前的具体状态对象来处理。
- 抽象状态角色(State): 定义一个接口以封装使用上下文环境的的一个特定状态相关的行为。
具体状态角色(Concrete State):实现抽象状态定义的接口。
示例图如下:
这里为了方便理解,我们依旧使用一个简单的示例来加以说明。
我们在使用耳机听音乐的时候,一般会有两个状态,播放和暂停,按一下是从暂停到播放,再按一下就是从播放到暂停。那么我们可以根据这个场景来使用状态模式进行开发!
首先依旧定义一个抽象状态角色,用于表示音乐的状态,并指定该类的行为,也就是方法,这个抽象类的代码如下:
interface MusicState{
void press();
}
定义好该抽象类之后,我们再来定义具体状态角色类。这里定义两个状态,一个是播放状态,一个是暂停状态,代码如下:
class PlayState implements MusicState{
@Override
public void press() {
System.out.println("播放音乐!");
}
}
class PauseState implements MusicState{
@Override
public void press() {
System.out.println("暂停音乐!");
}
}
然后在来定义一个环境角色,用于对客户端提供一个接口,并对状态进行处理。代码如下:
class Headset{
private MusicState state;
private int i;
public Headset(MusicState state){
this.state=state;
}
public void press() {
if((i&1)==0) {
this.state=new PlayState();
}else {
this.state=new PauseState();
}
this.state.press();
i++;
}
public MusicState getState() {
return state;
}
public void setState(MusicState state) {
this.state = state;
}
}
最后再来进行测试,测试代码如下:
public static void main(String[] args) {
Headset hs = new Headset(new PlayState());
//第一次播放音乐
hs.press();
//第二次暂停音乐
hs.press();
//第三次播放音乐
hs.press();
}
输出结果:
播放音乐!
暂停音乐!
播放音乐!
状态模式优点:
扩展性好,将和状态有关的行为放到一起,增加新的的状态,只需要改变对象状态即可改变对象的行为即可;
复用性好,让多个环境对象共享一个状态对象,从而减少系统中对象的个数;
状态模式缺点:
使用状态模式会增加系统类和对象的个数,并且该模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱;
状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。
使用场景:
行为随状态改变而改变的场景;
条件、分支语句的代替者。
注意事项 :
在行为受状态约束的时候使用状态模式,而且状态不超过5个。
** 和策略模式比较:**
在学习状态模式的时候,很容易和策略模式搞混,因为它们实在是太像了,很难区分,在查阅一番资料之后,整理了如下的相同点和区别点。
相同点:
- 它们很容易添加新的状态或策略,而且不需要修改使用它们的Context对象。
- 它们都符合OCP原则,在状态模式和策略模式中,Context对象对修改是关闭的,添加新的状态或策略,都不需要修改Context。
- 它们都会初始化。
- 它们都依赖子类去实现相关行为。
区别点
- 状态模式的行为是平行性的,不可相互替换的;
- 而策略模式的行为是平等性的,是可以相互替换的。
- 最重要的一个不同之处是,策略模式的改变由客户端完成;
- 而状态模式的改变,由环境角色或状态自己.
Java设计模式之十二 ---- 备忘录模式和状态模式的更多相关文章
- Java设计模式(十二) 策略模式
原创文章,同步发自作者个人博客,http://www.jasongj.com/design_pattern/strategy/ 策略模式介绍 策略模式定义 策略模式(Strategy Pattern) ...
- Java进阶篇设计模式之十二 ---- 备忘录模式和状态模式
前言 在上一篇中我们学习了行为型模式的策略模式(Strategy Pattern)和模板模式(Template Pattern).本篇则来学习下行为型模式的两个模式,备忘录模式(Memento Pat ...
- Java设计模式(十) 备忘录模式 状态模式
(十九)备忘录模式 备忘录模式目的是保存一个对象的某个状态,在适当的时候恢复这个对象. class Memento{ private String value; public Memento(Stri ...
- Java设计模式菜鸟系列(十三)建模和实现状态模式
转载请注明出处:http://blog.csdn.net/lhy_ycu/article/details/39829859 状态模式(State):同意对象在内部状态改变时改变它的行为,对象看起来好像 ...
- Java 设计模式系列(二十)状态模式
Java 设计模式系列(二十)状态模式 状态模式,又称状态对象模式(Pattern of Objects for States),状态模式是对象的行为模式.状态模式允许一个对象在其内部状态改变的时候改 ...
- 桥接模式 桥梁模式 bridge 结构型 设计模式(十二)
桥接模式Bridge Bridge 意为桥梁,桥接模式的作用就像桥梁一样,用于把两件事物连接起来 意图 将抽象部分与他的实现部分进行分离,使得他们都可以独立的发展. 意图解析 依赖倒置原 ...
- Java设计模式(15)备忘录模式(Memento模式)
Memento定义:memento是一个保存另外一个对象内部状态拷贝的对象,这样以后就可以将该对象恢复到原先保存的状态. Memento模式相对也比较好理解,我们看下列代码: public class ...
- Java 设计模式系列(二二)责任链模式
Java 设计模式系列(二二)责任链模式 责任链模式是一种对象的行为模式.在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链.请求在这个链上传递,直到链上的某一个对象决定处理此请求 ...
- 疯狂JAVA讲义---第十二章:Swing编程(五)进度条和滑动条
http://blog.csdn.net/terryzero/article/details/3797782 疯狂JAVA讲义---第十二章:Swing编程(五)进度条和滑动条 标签: swing编程 ...
随机推荐
- transformer 源码
训练时: 1. 输入正确标签一次性解码出来 预测时: 1. 第一次输入1个词,解码出一个词 第二次输入第一次输入的词和第一次解码出来词一起,解码出来第3个词,这样依次解码,解码到最长的长度或者< ...
- SpringBoot2.0源码分析(二):整合ActiveMQ分析
SpringBoot具体整合ActiveMQ可参考:SpringBoot2.0应用(二):SpringBoot2.0整合ActiveMQ ActiveMQ自动注入 当项目中存在javax.jms.Me ...
- [EOJ439] 强制在线
Description 见EOJ439 Solution 先考虑不强制在线怎么做. 按询问区间右端点排序,从左往右扫,维护所有后缀的答案. 如果扫到 \(a[i]\),那么让统计个数的 \(cnt[a ...
- MySQL基准测试(二)--方法
MySQL基准测试(二)--方法 目的: 方法不是越高级越好.而应该善于做减法.至简是一种智慧,首先要做的是收集MySQL的各状态数据.收集到了,不管各个时间段出现的问题,至少你手上有第一时间的状态数 ...
- Ado.Net实体数据模型EF,如何在代码中添加数据库连接密码
在创建EF模型的时候,VS2013提示说“在连接字符串中存储敏感数据可能有安全风险”,于是我选择了在代码中添加,可是如何通过代码添加呢? 我在网上百度了下,没有人说的清楚直观. 假设我们创建了一个名字 ...
- T-SQL:Varchar和Nvarchar区别(八)
常规数据类型:CHAR 和 VARCHAR Unicode 数据类型 NCHAR NVARCHAR 常规数据类型 会限制除英语之外语言 Unicode 会支持多种语言 VAR 区别 : 1. ...
- CentOS6.5安装mysql以及常见问题的解决
前言 最近在学习Linux系统,今天在安装MySQL数据库时出现很多问题,花费了两个小时终于解决,故记录下来以供大家参考.(本人目前还在学习阶段,下面写到的是自己结合网上查到的资料以及各位前辈给出的解 ...
- 【Spring】24、<load-on-startup>0</load-on-startup>配置
load-on-startup标记容器是否在启动的时候实例化并调用其init()方法的优先级. 它的值表示servlet应该被载入的顺序 当值为0或者大于0时,表示容器在应用启动时就加载并初始化这个s ...
- Django Rest framework 之 认证
django rest framework 官网 django rest framework 之 认证(一) django rest framework 之 权限(二) django rest fra ...
- HttpHandler与HttpModule介绍
前言:作为一个开发人员,我们看过很多的关于开发的书,但是都是教我们"知其然",并没有教我们"知其所以然",我们开发web项目的过程中,当我们输完URL敲下回车就 ...