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

前言

上一篇中咱们学习了行为型模式的策略模式(Strategy Pattern)和模板模式(Template Pattern)。本篇则来学习下行为型模式的两个模式,备忘录模式(Memento Pattern)和状态模式(Memento Pattern)。html

备忘录模式

简介java

备忘录模式(Memento Pattern)用于保存一个对象的某个状态,以便在适当的时候恢复对象,该模式属于行为型模式。
其主要目的是在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象以外保存这个状态。git

备忘录模式,其主要的的思想就是备份。例如,txt、word等文档的保存,游戏的存档,操做系统的备份,也包括咱们常常用的快捷键Ctrl+Z等等。除了以上的这些应用,咱们在编程中接触最多的估计就是数据库的事物了,它提供了一种恢复机制,可在出现异常的时候进行还原。github

备忘录模式主要由这三个角色组成,备忘录角色(Memento)、发起人角色(Originator)和负责人(Caretaker)角色。数据库

  • 备忘录(Memento):主要的功能是包含要被恢复的对象的状态。
  • 发起人(Originator):在建立的时候,会在备忘录对象中存储状态。
  • 负责人(Caretaker):主要是负责从备忘录对象中恢复对象的状态。

示例图以下:
在这里插入图片描述编程

咱们这里依旧用一个示例来进行说明吧。
咱们在玩游戏有的时候,会常常用到一个游戏功能,那就是存档。主要是为了保存游戏进度,防止信息丢失,而且也能够经过进行读档恢复保存的信息。好比xuwujing在玩一个游戏的时候,建立好角色以后进行打怪练级,而后挑战BOSS,不过担忧挑战失败,因而便在挑战前进行存档,存档成功以后,再来进行挑战BOSS。
那么咱们能够根据这个场景来使用备忘录模式来进行开发。网络

首先定义一个Memento,也就是游戏存档的信息,主要存储游戏人物等级和生命值。ide

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-study是本人在学习Java过程当中记录的一些代码,也包括以前博文中使用的代码。若是感受不错,但愿顺手给个start,固然若是有不足,也但愿提出。
github地址: https://github.com/xuwujing/java-study

原创不易,若是感受不错,但愿给个推荐!您的支持是我写做的最大动力! 版权声明: 做者:虚无境 博客园出处:http://www.cnblogs.com/xuwujing CSDN出处:http://blog.csdn.net/qazwsxpcm  我的博客出处:http://www.panchengming.com

相关文章
相关标签/搜索