【设计模式】JAVA Design Patterns——Command(事务模式)

简介: 【设计模式】JAVA Design Patterns——Command(事务模式)

🔍目的


将请求封装为对象,从而使你可以将具有不同请求的客户端参数化,队列或记录请求,并且支持可撤销操作。


🔍解释


真实世界例子

有一个巫师在地精上施放咒语。咒语在地精上一一执行。第一个咒语使地精缩小,第二个使他不可见。然后巫师将咒语一个个的反转。这里的每一个咒语都是一个可撤销的命令对象。


通俗描述

用命令对象的方式存储请求以在将来时可以执行它或撤销它。


维基百科

在面向对象编程中,命令模式是一种行为型设计模式,它把在稍后执行的一个动作或触发的一个事件所需要的所有信息封装到一个对象中。


程序示例

创建一个巫师类

public class Wizard {
 
  private static final Logger LOGGER = LoggerFactory.getLogger(Wizard.class);
 
  private final Deque<Runnable> undoStack = new LinkedList<>();
  private final Deque<Runnable> redoStack = new LinkedList<>();
 
  public Wizard() {}
 
  public void castSpell(Command command, Target target) {
    LOGGER.info("{} casts {} at {}", this, command, target);
    command.execute(target);
    undoStack.offerLast(command);
  }
 
  public void undoLastSpell() {
    if (!undoStack.isEmpty()) {
      var previousSpell = undoStack.pollLast();
      redoStack.offerLast(previousSpell);
      LOGGER.info("{} undoes {}", this, previousSpell);
      previousSpell.undo();
    }
  }
 
  public void redoLastSpell() {
    if (!redoStack.isEmpty()) {
      var previousSpell = redoStack.pollLast();
      undoStack.offerLast(previousSpell);
      LOGGER.info("{} redoes {}", this, previousSpell);
      previousSpell.redo();
    }
  }
 
  @Override
  public String toString() {
    return "Wizard";
  }
}


创建咒语层级

public interface Command {
 
  void execute(Target target);
 
  void undo();
 
  void redo();
 
  String toString();
}
 
public class InvisibilitySpell implements Command {
 
  private Target target;
 
  @Override
  public void execute(Target target) {
    target.setVisibility(Visibility.INVISIBLE);
    this.target = target;
  }
 
  @Override
  public void undo() {
    if (target != null) {
      target.setVisibility(Visibility.VISIBLE);
    }
  }
 
  @Override
  public void redo() {
    if (target != null) {
      target.setVisibility(Visibility.INVISIBLE);
    }
  }
 
  @Override
  public String toString() {
    return "Invisibility spell";
  }
}
 
public class ShrinkSpell implements Command {
 
  private Size oldSize;
  private Target target;
 
  @Override
  public void execute(Target target) {
    oldSize = target.getSize();
    target.setSize(Size.SMALL);
    this.target = target;
  }
 
  @Override
  public void undo() {
    if (oldSize != null && target != null) {
      var temp = target.getSize();
      target.setSize(oldSize);
      oldSize = temp;
    }
  }
 
  @Override
  public void redo() {
    undo();
  }
 
  @Override
  public String toString() {
    return "Shrink spell";
  }
}


创建咒语的目标:地精

public abstract class Target {
 
  private static final Logger LOGGER = LoggerFactory.getLogger(Target.class);
 
  private Size size;
 
  private Visibility visibility;
 
  public Size getSize() {
    return size;
  }
 
  public void setSize(Size size) {
    this.size = size;
  }
 
  public Visibility getVisibility() {
    return visibility;
  }
 
  public void setVisibility(Visibility visibility) {
    this.visibility = visibility;
  }
 
  @Override
  public abstract String toString();
 
  public void printStatus() {
    LOGGER.info("{}, [size={}] [visibility={}]", this, getSize(), getVisibility());
  }
}
 
public class Goblin extends Target {
 
  public Goblin() {
    setSize(Size.NORMAL);
    setVisibility(Visibility.VISIBLE);
  }
 
  @Override
  public String toString() {
    return "Goblin";
  }
 
}


实践示例

var wizard = new Wizard();
var goblin = new Goblin();
goblin.printStatus();
// Goblin, [size=normal] [visibility=visible]
wizard.castSpell(new ShrinkSpell(), goblin);
// Wizard casts Shrink spell at Goblin
goblin.printStatus();
// Goblin, [size=small] [visibility=visible]
wizard.castSpell(new InvisibilitySpell(), goblin);
// Wizard casts Invisibility spell at Goblin
goblin.printStatus();
// Goblin, [size=small] [visibility=invisible]
wizard.undoLastSpell();
// Wizard undoes Invisibility spell
goblin.printStatus();
// Goblin, [size=small] [visibility=visible]


🔍类图

74ba66885d8b4b11ac7faad0ab92fb11.png


🔍适用场景

使用命令模式情况:

  • 通过操作将对象参数化。您可以使用回调函数(即,已在某处注册以便稍后调用的函数)以过程语言表示这种参数化。命令是回调的一种面向对象替代方案。
  • 在不同的时间指定,排队和执行请求。一个命令对象的生存期可以独立于原始请求。如果请求的接收方可以以地址空间无关的方式来表示,那么你可以将请求的命令对象传输到其他进程并在那里执行请求。
  • 支持撤销。命令的执行操作可以在命令本身中存储状态以反转其效果。命令接口必须有添加的反执行操作,该操作可以逆转上一次执行调用的效果。执行的命令存储在历史列表中。无限撤消和重做通过分别向后和向前遍历此列表来实现,分别调用unexecute和execute。
  • 支持日志记录更改,以便在系统崩溃时可以重新应用它们。通过使用加载和存储操作扩展命令接口,你可以保留更改的永久日志。从崩溃中恢复涉及从磁盘重新加载记录的命令,并通过执行操作重新执行它们。
  • 通过原始的操作来构建一个以高级操作围绕的系统。这种结构在支持事务的信息系统中很常见。事务封装了一组数据更改。命令模式提供了一种对事务进行建模的方法。命令具有公共接口,让你以相同的方式调用所有事务。该模式还可以通过新的事务来轻松扩展系统。

8724ef1944dc4e9b9f2ad9efd7014784.gif

相关文章
|
1天前
|
设计模式 人工智能 自然语言处理
【设计模式】MVVM模式在AI大模型领域的创新应用
【设计模式】MVVM模式在AI大模型领域的创新应用
10 0
|
6天前
|
设计模式 存储 安全
Java中的23种设计模式
Java中的23种设计模式
9 1
|
12天前
|
设计模式 新零售 Java
设计模式最佳套路5 —— 愉快地使用工厂方法模式
工厂模式一般配合策略模式一起使用,当系统中有多种产品(策略),且每种产品有多个实例时,此时适合使用工厂模式:每种产品对应的工厂提供该产品不同实例的创建功能,从而避免调用方和产品创建逻辑的耦合,完美符合迪米特法则(最少知道原则)。
32 6
|
12天前
|
设计模式 Java 关系型数据库
设计模式第2弹:工厂方法模式
type ComputerProduct struct{} // 实现工厂方法 func (computer ComputerProduct) GetInformation() string { return "电脑,官方称呼计算机,主要用于进行数据运算的一台机器。" }
24 4
|
12天前
|
设计模式 XML Java
【设计模式】第三篇:一篇搞定工厂模式【简单工厂、工厂方法模式、抽象工厂模式】
三 结尾 如果文章中有什么不足,欢迎大家留言交流,感谢朋友们的支持! 如果能帮到你的话,那就来关注我吧!如果您更喜欢微信文章的阅读方式,可以关注我的公众号
19 5
|
13天前
|
设计模式 架构师 NoSQL
设计模式-工厂方法模式和抽象工厂模式
 每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度。
189 0
|
20天前
|
JavaScript Java 测试技术
Java项目基于ssm+vue.js的校园事务自助指南服务系统附带文章和源代码设计说明文档ppt
Java项目基于ssm+vue.js的校园事务自助指南服务系统附带文章和源代码设计说明文档ppt
12 0
|
21天前
|
设计模式 存储 前端开发
Java的mvc设计模式在web开发中应用
Java的mvc设计模式在web开发中应用
|
24天前
|
设计模式 安全 Java
【设计模式】JAVA Design Patterns——Curiously Recurring Template Pattern(奇异递归模板模式)
该文介绍了一种C++的编程技巧——奇异递归模板模式(CRTP),旨在让派生组件能继承基本组件的特定功能。通过示例展示了如何创建一个`Fighter`接口和`MmaFighter`类,其中`MmaFighter`及其子类如`MmaBantamweightFighter`和`MmaHeavyweightFighter`强制类型安全,确保相同重量级的拳手之间才能进行比赛。这种设计避免了不同重量级拳手间的错误匹配,编译时会报错。CRTP适用于处理类型冲突、参数化类方法和限制方法只对相同类型实例生效的情况。
【设计模式】JAVA Design Patterns——Curiously Recurring Template Pattern(奇异递归模板模式)
|
1天前
|
Java 开发者
线程的诞生之路:Java多线程创建方法的抉择与智慧
【6月更文挑战第19天】Java多线程编程中,开发者可选择继承Thread类或实现Runnable接口。继承Thread直接但受限于单继承,适合简单场景;实现Runnable更灵活,支持代码复用,适用于如银行转账这类需多线程处理的复杂任务。在资源管理和任务执行控制上,Runnable接口通常更优。