前言

上一篇中我们学习了行为型模式的策略模式(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个。

** 和策略模式比较:**
在学习状态模式的时候,很容易和策略模式搞混,因为它们实在是太像了,很难区分,在查阅一番资料之后,整理了如下的相同点和区别点。

相同点:

  1. 它们很容易添加新的状态或策略,而且不需要修改使用它们的Context对象。
  2. 它们都符合OCP原则,在状态模式和策略模式中,Context对象对修改是关闭的,添加新的状态或策略,都不需要修改Context。
  3. 它们都会初始化。
  4. 它们都依赖子类去实现相关行为。

区别点

    1. 状态模式的行为是平行性的,不可相互替换的;
    2. 而策略模式的行为是平等性的,是可以相互替换的。
    3. 最重要的一个不同之处是,策略模式的改变由客户端完成;
    4. 而状态模式的改变,由环境角色或状态自己.

Java设计模式之十二 ---- 备忘录模式和状态模式的更多相关文章

  1. Java设计模式(十二) 策略模式

    原创文章,同步发自作者个人博客,http://www.jasongj.com/design_pattern/strategy/ 策略模式介绍 策略模式定义 策略模式(Strategy Pattern) ...

  2. Java进阶篇设计模式之十二 ---- 备忘录模式和状态模式

    前言 在上一篇中我们学习了行为型模式的策略模式(Strategy Pattern)和模板模式(Template Pattern).本篇则来学习下行为型模式的两个模式,备忘录模式(Memento Pat ...

  3. Java设计模式(十) 备忘录模式 状态模式

    (十九)备忘录模式 备忘录模式目的是保存一个对象的某个状态,在适当的时候恢复这个对象. class Memento{ private String value; public Memento(Stri ...

  4. Java设计模式菜鸟系列(十三)建模和实现状态模式

    转载请注明出处:http://blog.csdn.net/lhy_ycu/article/details/39829859 状态模式(State):同意对象在内部状态改变时改变它的行为,对象看起来好像 ...

  5. Java 设计模式系列(二十)状态模式

    Java 设计模式系列(二十)状态模式 状态模式,又称状态对象模式(Pattern of Objects for States),状态模式是对象的行为模式.状态模式允许一个对象在其内部状态改变的时候改 ...

  6. 桥接模式 桥梁模式 bridge 结构型 设计模式(十二)

      桥接模式Bridge   Bridge 意为桥梁,桥接模式的作用就像桥梁一样,用于把两件事物连接起来   意图 将抽象部分与他的实现部分进行分离,使得他们都可以独立的发展.  意图解析 依赖倒置原 ...

  7. Java设计模式(15)备忘录模式(Memento模式)

    Memento定义:memento是一个保存另外一个对象内部状态拷贝的对象,这样以后就可以将该对象恢复到原先保存的状态. Memento模式相对也比较好理解,我们看下列代码: public class ...

  8. Java 设计模式系列(二二)责任链模式

    Java 设计模式系列(二二)责任链模式 责任链模式是一种对象的行为模式.在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链.请求在这个链上传递,直到链上的某一个对象决定处理此请求 ...

  9. 疯狂JAVA讲义---第十二章:Swing编程(五)进度条和滑动条

    http://blog.csdn.net/terryzero/article/details/3797782 疯狂JAVA讲义---第十二章:Swing编程(五)进度条和滑动条 标签: swing编程 ...

随机推荐

  1. android开发(0):android studio的下载安装与简单使用 | sdk的安装与编译运行

    android studio,简称AS,是集成开发环境,所谓集成,就是集编辑.编译.调试.打包等于一体.简单来说,通过AS,就可以开发出在android系统上运行的APP. 我使用的是macos系统. ...

  2. 解决 "Script Error" 的另类思路

    本文由小芭乐发表 前端的同学如果用 window.onerror 事件做过监控,应该知道,跨域的脚本会给出 "Script Error." 提示,拿不到具体的错误信息和堆栈信息. ...

  3. Linux 安装 JDK

    本篇博客用于记录一下在 Linux 系统下安装 Java 环境. 在大部分的 Linux 系统中都有安装 Open JDK,所以最好是先卸载 Open JDK 后在进行我们的 JDK 安装.Open ...

  4. 一个简单有趣的Python音乐播放器

    (赠新手,老鸟绕行0.0) Python版本:3.5.2 源码如下: __Author__ = "Lance#" # -*- coding = utf-8 -*- #导入相应模块 ...

  5. [转]ui-grid User can't select the row by clicking the select checkbox available in the respective row when enableFullRowSelection : true"

    本文转自:https://github.com/angular-ui/ui-grid/issues/5239 Try this style to enable checkbox selection: ...

  6. ajax读取txt文本时乱码的解决方案

    前言:第一次学习使用 ajax 就是用来读取文本 先给出现乱码的代码<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional/ ...

  7. 正则表达式--C#正则表达式的符号及例子

    正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符.及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑. C#中经常会遇到要查找某一个字 ...

  8. [PHP]算法- 二叉树的深度的PHP实现

    二叉树的深度: 输入一棵二叉树,求该树的深度.从根结点到叶结点依次经过的结点(含根.叶结点)形成树的一条路径,最长路径的长度为树的深度. 思路: 1.非递归层序遍历 2.使用辅助队列,根结点先入队列 ...

  9. 设计模式之迭代器模式(Iterator)

    迭代器在STL运用广泛,类似容器的迭代已经成为其重要特性,而迭代器模式则是利用迭代器概念进行的抽象运用,迭代器模式运用广泛和有用,因为其能够不考虑数据的存储方式,而是直接面对数据进行迭代,也就是说我们 ...

  10. Vue 系列之 渲染与事件处理

    渲染相关 列表渲染 与 条件渲染 Vue 中的常见的渲染有 列表渲染 和 条件渲染 所谓条件渲染,则是通过添加一定的逻辑条件来进行 Dom 元素的操作 v-if v-else v-else-if &l ...