备忘录模式和状态模式
目录
1、备忘录模式
1.1 什么是备忘录模式
备忘录模式(Memento Pattern):在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。它是一种对象行为型模式,其别名为Token。
我们平时经常用到的撤销操作,浏览器回退,Git版本链控制管理等等,都是备忘录的体现。
1.2 结构图
- 备忘录模式共分为三种种角色:
- Originator(原发器):需要被记录状态的类
- Memento(备忘录):记录原发器状态的类,内部含有原发器的部分属性或者全部属性,不能被其他类所修改
- Caretaker(管理者):管理备忘录(重点)
- 原发器创建备忘录记录此刻内部各属性的值,将备忘录交给管理者管理,回滚状态时,从管理者中获取备忘录,重置内部属性
1.3 实例演示
平时我们进行文件编辑时,都会喜欢保存文件,这样当我们需要回退时,可以进行撤销操作,找回之前的完整内容。下面就用一个实例演示一下文件的保存和撤销的过程。
/**
* @description: 编辑者(原发者)
* @author: zps
* @create: 2020-05-05 10:10
**/
@Data
public class Editor {
private String title;
private String context;
private String imgs;
//创建备忘录
public ArticleMemento saveToMemento(){
ArticleMemento articleMemento = new ArticleMemento(this.title, this.context, this.imgs);
return articleMemento;
}
//根据备忘录的内容进行回滚
public void undoFromMemento(ArticleMemento articleMemento){
this.title = articleMemento.getTitle();
this.context = articleMemento.getContext();
this.imgs = articleMemento.getImgs();
}
}
/**
* @description:
* @author: zps
* @create: 2020-05-05 10:13
**/
@Data
@AllArgsConstructor
public class ArticleMemento {
private String title;
private String context;
private String imgs;
}
/**
* @description: 草稿箱管理者
* @author: zps
* @create: 2020-05-05 10:18
**/
public class Carktaker {
private final Stack<ArticleMemento> stack = new Stack<>();
//回滚
public ArticleMemento getMemonto(){
return stack.pop();
}
//添加备忘录
public void addMemonto(ArticleMemento articleMemento){
stack.push(articleMemento);
}
}
**
* @description: 测试
* @author: zps
* @create: 2020-05-05 10:22
**/
public class Test {
public static void main(String[] args) {
//草稿箱
Carktaker carktaker = new Carktaker();
Editor editor = new Editor();
editor.setTitle("设计模式------备忘录模式");
editor.setContext("备忘录模式:在不破坏封装的前提下它是一种对象行为型模式,其别名为Token。");
editor.setImgs("c://a.img");
ArticleMemento articleMemento = editor.saveToMemento();
carktaker.addMemonto(articleMemento);
System.out.println("保存成功!");
System.out.println("保存内容为:" + editor);
System.out.println("------首次修改文章-------");
editor.setContext("暂时没有内容");
System.out.println("修改后完整内容:" + editor);
articleMemento = editor.saveToMemento();
carktaker.addMemonto(articleMemento);
System.out.println("保存到了草稿箱");
System.out.println("这时发现刚刚的操作是无操,想回退到上一个版本");
articleMemento = carktaker.getMemonto();
articleMemento = carktaker.getMemonto();
editor.undoFromMemento(articleMemento);
System.out.println("回退成功:");
System.out.println("文章内容:" + editor);
}
}
运行结果:
1.4 小结
备忘录模式的主要优点如下:
- 它提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤,当新的状态无效或者存在问题时,可以使用暂时存储起来的备忘录将状态复原。
- 备忘录实现了对信息的封装,一个备忘录对象是一种原发器对象状态的表示,不会被其他代码所改动。备忘录保存了原发器的状态,采用列表、堆栈等集合来存储备忘录对象可以实现多次撤销操作。
备忘录模式的主要缺点如下:
- 资源消耗过大,如果需要保存的原发器类的成员变量太多,就不可避免需要占用大量的存储空间,每保存一次对象的状态都需要消耗一定的系统资源。
适用场景:
- 保存一个对象在某一个时刻的全部状态或部分状态,这样以后需要时它能够恢复到先前的状态,实现撤销操作。
- 防止外界对象破坏一个对象历史状态的封装性,避免将对象历史状态的实现细节暴露给外界对象。
2、状态模式
2.1 什么是状态模式
状态模式就是复杂对象不同状态下的行为封装与状态转换。
状态模式中的行为由状态决定,不同的状态下有不同的行为。其意图是让一个对象在其内部改变时候,其行为也随之改变。状态模式核心是状态与行为绑定,不同的状态对应不同的行为。
2.2 结构图
- 状态模式共分为三种角色:
- Context(上下文环境):将自身的状态与行为分离出去,封装成状态类,持有抽象状态类的引用,根据自身属性变换更换具体状态类。
- State(抽象状态类):具体状态类的抽象,可以持有上下文环境的引用来更换状态。
- Concrete State(具体状态类):实现不同状态下的不同行为。
- 上下文环境引用抽象状态类完成行为的调用,状态类的状态变化可以有上下文环境处理,也可以由状态类处理。
- 状态类可以共享,像开关对象一样,可以被多个上下文环境引用。
2.3 实例演示
代码示例:当用户未登录时,不可以进行评论和收藏,登录后可以对文章进行评论和收藏。
/**
* @description: 抽象状态角色
* @author: zps
* @create: 2020-05-05 11:42
**/
public abstract class UserState {
protected AppContext appContext;
public void setContext(AppContext appContext){
this.appContext = appContext;
}
public abstract void favorite();
public abstract void commnet(String comment);
}
/**
* @description: 未登录状态
* @author: zps
* @create: 2020-05-05 11:46
**/
public class UnloginState extends UserState {
@Override
public void favorite() {
login();
this.appContext.getState().favorite();
}
@Override
public void commnet(String comment) {
login();
this.appContext.getState().commnet(comment);
}
private void login(){
System.out.println("您未登录,正在跳转登录界面!");
System.out.println("登录成功!");
this.appContext.setState(this.appContext.STATE_LOGIN);
}
}
/**
* @description: 登录状态
* @author: zps
* @create: 2020-05-05 11:44
**/
public class LoginState extends UserState{
@Override
public void favorite() {
System.out.println("收藏成功!");
}
@Override
public void commnet(String comment) {
System.out.println(comment);
}
}
/**
* @description:上下文角色
* @author: zps
* @create: 2020-05-05 11:43
**/
public class AppContext {
//保存状态
public static final UserState STATE_LOGIN = new LoginState();
public static final UserState STATE_UNLOGIN = new UnloginState();
private UserState currentState = STATE_UNLOGIN;
{//状态与行为绑定
STATE_LOGIN.setContext(this);
STATE_UNLOGIN.setContext(this);
}
public void setState(UserState state){
this.currentState = state;
this.currentState.setContext(this);//状态与行为绑定
}
public UserState getState(){
return this.currentState;
}
public void favorite(){
this.currentState.favorite();
}
public void comment(String comment){
this.currentState.commnet(comment);
}
}
/**
* @description: 测试
* @author: zps
* @create: 2020-05-05 11:58
**/
public class Test {
public static void main(String[] args) {
AppContext appContext = new AppContext();
// appContext.favorite();
appContext.comment("这文章不错,给你点赞罗!!!");
}
运行结果:
2.4 小结
状态模式的优点:
- 结构清晰,避免了程序的复杂,提高了系统可维护性。
- 遵循了设计原则。很要的体现了开闭原则和单一职责原则,每个状态都是一个子类,与单一职责原则高度符合,扩展状态只需增加子类,正是开闭原则的体现。封装性与迪米特法则也非常吻合。
状态模式的缺点:
- 只有一个缺点,如果一个事务有很多个状态,就会造成子类太多的问题。需要在项目使用时来衡量是否使用状态模式。
状态模式的使用场景:
- 行为随状态改变而改变的的场景,例如权限设计,人员对象权限不同执行相同的行为其结果也会不同。
- 条件、分支判断语句替代者,通过扩展子类实现条件判断处理。
状态模式与策略模式的区别:
- 策略模式与状态模式在实现上有共同之处,都是把不同的情形抽象为统一的接口来实现,就放在一起进行记录。2个模式的UML建模图基本相似,区别在于状态模式需要在子类实现与context相关的一个状态行为。
- 状态模式的的思想是,状态之间的切换,在状态A执行完毕后自己控制状态指向状态B。状态模式是不停的切换状态执行。
- 策略模式的思想上是,考虑多种不同的业务规则将不同的算法封装起来,便于调用者选择调用。策略模式只是条件选择执行一次。