Java - GoF设计模式详解16(命令模式)
作者:hangge | 2023-06-13 10:05
十六、命令模式
1,基本介绍
(1)命令模式(Command )提供了一种将请求封装成对象的方法,从而使我们可以用不同的请求对客户进行参数化。
(2)命令模式的优点包括:
- 可以将请求的发送者和接收者解耦。发送者和接收者可以独立地变化。
- 可以支持可撤销操作。
- 可以方便地实现对请求的日志记录、恢复和重做。
- 可以将复杂的请求封装成一个单独的命令对象,这样就可以将该命令对象存储、传递或在队列中传递。
(3)该模式中包含的角色及其职责如下:
- 命令(Command):定义命令的接口,声明执行的操作。
- 具体命令(Concrete Command):命令的实现,通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。
- 接收者(Receiver):真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。
- 调用者(Invoker):负责调用命令。通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。
- 客户端(Client):创建具体命令对象,并设定它的接收者。注意这个不是我们常规意义上的客户端,而是在组装命令对象和接收者,或许,把这个 Client 称为装配者会更好理解,因为真正使用命令的客户端是从 Invoker 来触发执行。
2,使用样例
(1)下面我们演示如何在应用程序中使用命令模式来控制电灯。首先我们定义一个 Light 类(即接收者),该类表示电灯,并提供了 turnOn() 和 turnOff() 方法用于打开和关闭电灯。
// 电灯 public class Light { public void turnOn() { System.out.println("打开电灯"); } public void turnOff() { System.out.println("关闭电灯"); } }
(2)然后我们定义一个 Command 接口,该接口有一个 execute() 方法,用于执行命令。
// 命令接口 public interface Command { void execute(); }
(3)接着我们定义了两个实现了命令接口的具体命令类:TurnOnLightCommand 和 TurnOffLightCommand,分别用于打开和关闭电灯。
// 开灯命令 public class TurnOnLightCommand implements Command { private Light light; public TurnOnLightCommand(Light light) { this.light = light; } @Override public void execute() { light.turnOn(); } } // 关灯命令 public class TurnOffLightCommand implements Command { private Light light; public TurnOffLightCommand(Light light) { this.light = light; } @Override public void execute() { light.turnOff(); } }
(4)接下来我们定义了 RemoteControl 类,该类表示遥控器(即调用者 Invoker),并提供了 setCommand() 方法用于设置命令,以及 pressButton() 方法用于执行命令。
// 遥控器 public class RemoteControl { private Command command; public void setCommand(Command command) { this.command = command; } public void pressButton() { command.execute(); } }
(5)最后在应用程序的 main() 方法中(该类即为客户端 Client),我们创建了一个电灯对象,然后创建了打开和关闭电灯命令的实例。接着我们创建了遥控器对象,并使用 setCommand() 方法设置打开电灯命令,然后调用 pressButton() 方法执行命令。最后,我们再次使用 setCommand() 方法设置关闭电灯命令,并再次调用 pressButton() 方法执行命令。
public class Test { public static void main(String[] args) { // 创建一个电灯对象 Light light = new Light(); // 创建打开和关闭电灯命令的实例 Command turnOnCommand = new TurnOnLightCommand(light); Command turnOffCommand = new TurnOffLightCommand(light); // 创建遥控器对象 RemoteControl remoteControl = new RemoteControl(); // 设置打开电灯命令 remoteControl.setCommand(turnOnCommand); // 执行命令 remoteControl.pressButton(); // 设置关闭电灯命令 remoteControl.setCommand(turnOffCommand); // 执行命令 remoteControl.pressButton(); } }
附一:JDK 中的命令模式
(1)在 JDK 中,命令模式通常用于实现 GUI 组件的事件处理和菜单项的操作。例如,在 Swing 中,我们可以使用 java.awt.event.Action 接口来实现命令模式。Action 接口有一个 actionPerformed 方法,该方法可以被用作事件处理程序,当用户单击菜单项或按钮时,该方法会被调用。
(2)下面示例中我们创建了一个按钮和一个文本框,并创建了一个实现 Action 接口的命令类。当用户点击按钮时,这个命令的 actionPerformed 方法将会被调用,并在文本框中输出信息。
public class Test { public static void main(String[] args) { // 创建一个窗口 JFrame frame = new JFrame("My Window"); frame.setLayout(new BorderLayout()); frame.setSize(400, 300); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 创建一个文本字段 JTextField textField = new JTextField(); frame.add(textField, BorderLayout.CENTER); // 创建一个按钮 JButton button = new JButton("点击按钮"); frame.add(button,BorderLayout.NORTH); // 创建一个命令,当用户点击按钮时,将会执行这个命令 Action command = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { textField.setText("按钮被点击!"); } }; // 将命令附加到按钮上 button.addActionListener(command); // 显示窗口 frame.setVisible(true); } }
附二:Spring 中的命令模式
1,Spring 中的事务管理
(1)Spring 使用命令模式来封装事务操作,将事务操作作为命令对象进行封装,然后将其交给事务管理器进行处理。
(2)在 Spring 中,事务管理是通过 PlatformTransactionManager 接口来实现的。Sping 事务管理使用了命令模式,将事务操作封装为了 PlatformTransactionManager 接口的实现类,然后将其交给事务管理器进行处理。PlatformTransactionManager 接口定义了一系列的事务操作方法,如下所示:
接口说明:TransactionDefinition 接口定义了事务定义相关的属性,如事务传播行为、隔离级别、超时时间等。TransactionStatus 接口定义了事务状态相关的属性和操作方法,如是否新事务、是否已经被标记为回滚、是否已经完成等。
public interface PlatformTransactionManager { TransactionStatus getTransaction(TransactionDefinition var1) throws TransactionException; void commit(TransactionStatus var1) throws TransactionException; void rollback(TransactionStatus var1) throws TransactionException; }
(3)下面是一个示例代码,展示了如何使用 Spring 中的事务管理。UserService 类使用 @Transactional 注解标注了 addUser() 方法,表示该方法需要进行事务管理。在执行 addUser() 方法时,Spring 会自动调用 PlatformTransactionManager 接口的实现类进行事务操作,实现事务的开启、提交、回滚等功能。
@Service public class UserService { @Autowired private UserDao userDao; @Autowired private PlatformTransactionManager transactionManager; @Transactional public void addUser(User user) { userDao.addUser(user); } }
2,Spring 中的异步处理
(1)Spring 使用命令模式来实现异步处理功能,将异步处理操作封装为命令对象,然后交给异步任务执行器进行处理。
(2)在 Spring 中,可以通过 @Async 注解和 TaskExecutor 接口来实现异步处理功能。
- @Async 注解用于标注一个方法或类,表示该方法或类中的方法可以被异步执行。
- TaskExecutor 接口定义了一系列的异步任务执行方法,如下所示。
可以看到,Spring 中的异步处理使用了命令模式,将异步任务封装为了 Runnable 接口的实现类,然后交给 TaskExecutor 接口的实现类进行处理。
public interface TaskExecutor { void execute(Runnable var1); void execute(Runnable var1, long var2); }
(3)下面是一个示例代码,展示了如何使用 Spring 中的异步处理。UserService 类的 asyncAddUser() 方法使用了 @Async 注解,表示该方法可以被异步执行。当调用 asyncAddUser() 方法时,Spring 会自动将方法封装为 Runnable 接口的实现类,然后交给 TaskExecutor 接口的实现类进行处理。
@Service public class UserService { @Async public void asyncAddUser(User user) { // 进行用户添加操作 } }
3,Spring 中的定时调度任务
(1)Spring 使用命令模式来实现调度任务功能,将调度任务作为命令对象进行封装,然后交给调度器进行处理。
(2)在 Spring 中,可以使用 @Scheduled 注解和 TaskScheduler 接口来实现调度任务功能。
- @Scheduled 注解用于标注一个方法,表示该方法可以被定时执行。
- TaskScheduler 接口定义了一系列的调度任务执行方法,如下所示。
可以看到,Spring 中的调度任务使用了命令模式,将调度任务封装为了 Runnable 接口的实现类,然后交给 TaskScheduler 接口的实现类进行处理。
public interface TaskScheduler { ScheduledFuture schedule(Runnable var1, Trigger var2); ScheduledFuture schedule(Runnable var1, Date var2); ScheduledFuture scheduleAtFixedRate(Runnable var1, Date var2, long var3); ScheduledFuture scheduleAtFixedRate(Runnable var1, long var2); ScheduledFuture scheduleWithFixedDelay(Runnable var1, Date var2, long var3); ScheduledFuture scheduleWithFixedDelay(Runnable var1, long var2); }
(3)下面是一个示例代码,展示了如何使用 Spring 中的调度任务。ScheduledTask 类的 executeTask() 方法使用了 @Scheduled 注解,表示该方法可以被定时执行。当调度器启动时,Spring 会自动将 executeTask() 方法封装为 Runnable 接口的实现类,然后交给 TaskScheduler 接口的实现类进行处理,按照 cron 表达式规定的时间周期执行 executeTask() 方法。
@Component public class ScheduledTask { @Scheduled(cron = "0 0/1 * * * *") public void executeTask() { // 进行定时任务处理 } }
4,Spring 中的 JdbcTemplate 类
(1)Spring 中的 JdbcTemplate 类使用了命令模式,将 SQL 操作封装为命令对象,然后交给 JdbcTemplate 执行。提示:准确来说 JdbcTemplate 并不是遵从标准的命令模式,而是采用了命令模式思想。JdbcTemplate 不仅是调用者角色,其内部类 QueryStatementCallback 又充当了接收者和具体命令角色。
(2)下面是使用 JdbcTemplate 类的 query() 方法查询数据库样例:
@Repository public class UserDao { @Autowired JdbcTemplate jdbcTemplate; // 获取多条数据 public List<User> getAllUsers() { return jdbcTemplate.query("SELECT * FROM users", new BeanPropertyRowMapper<>(User.class)); } }
(3)而 query() 方法内容如下,可以看到这里面有个内部类 QueryStatementCallback 实现了 StatementCallback 接口,该接口只有唯一的 doInStatement 方法。最后就是创建了这个内部类的实例传给 execute 方法并放回结构。从源码可以发现:
- StatementCallback 接口可以看做是命令接口。
- 匿名内部类 QueryStatementCallback 是该命令接口的一个具体实现命令,同时也充当命令接收者。
(4)execute(StatementCallback<T> action) 方法代码如下,该方法内部调用 action.doInStatement 方法。不同的实现 StatementCallback 接口的对象,对应不同的 doInStatemnt 实现逻辑。因此 JdbcTemplate 是调用者角色。
全部评论(0)