命令模式
命令模式是一种行为设计模式,它的主要目的是将请求封装成一个对象,从而使得请求的发送者和接收者之间进行解耦。
在命令模式中,命令被封装为一个对象,包含了需要执行的操作以及执行这些操作所需的所有参数。
命令的发送者不需要知道接收者的具体情况,也不需要知道命令如何被执行,只需要将命令对象发送给接收者,由接收者来解析并执行命令。
应用场景
- 请求者创建命令,消费者根据命令动态做出响应的场景
- 撤销和恢复操作:例如文本编辑器中的撤销和恢复功能。
- 请求日志记录:可以记录用户操作的日志,以便后期查看。
- 任务队列:例如线程池中的任务队列,可以将命令放入队列中异步执行。
- 菜单项操作:例如图形界面中的菜单项单击、快捷键操作等。
命令模式中的角色
- 命令(Command): 命令是一个抽象的对象,封装了执行特定操作的方法。它通常包含一个执行操作的接口,可能还包含撤销操作的接口。
- 具体命令(Concrete Command): 具体命令是命令的具体实现,它实现了命令接口中定义的方法。 每一个具体命令都与一个特定的接收者相关联,负责调用接收者执行请求的操作。
- 接收者(Receiver): 接收者是实际执行命令操作的对象。它包含了命令执行时所需的具体逻辑。
- 调用者/发送者(Invoker/Sender): 调用者是命令的发送者,它负责创建命令对象并将其发送给接收者。
- 客户端(Client): 客户端创建具体命令对象,并将其与相应的接收者关联起来,然后将命令对象传递给调用者。
优点
- **解耦:**发送者和接收者之间的解耦,发送者不需要知道接收者的具体实现,只需要知道如何发送命令。
- **扩展性:**易于添加新的命令和接收者,不会影响到已有的代码。
- **支持撤销和重做操作:**可以通过保存命令历史来支持撤销和重做操作。
缺点
- 可能导致类爆炸:为每个操作创建一个具体的命令类可能会导致类爆炸,尤其是在具有大量命令的系统中。
- 命令的逻辑混乱:如果命令的逻辑比较复杂,命令模式可能会导致命令的逻辑混乱。
Java中的注意事项
- 合理的接口设计:命令接口应该设计得简洁清晰,易于使用。
- 抽象类的使用:可以使用抽象类来实现部分命令共同的行为,以避免重复代码。
- 命令的实现方式:根据业务需求选择合适的命令实现方式,如简单命令、宏命令等。
- 线程安全性:在多线程环境下使用命令模式时,需要考虑命令对象的线程安全性。
1.案例1-小厨房(JavaSE版本)
# 餐厅,服务员先根据客户创建订单,并将订单发送给厨师,厨师根据具体的订单生产
1.1.创建命令接口
/*** 抽象命令接口*/
public interface CookCommand {/*** 执行命令*/void execute();
}
1.2.创建传输的参数
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;@Getter @Setter
public class Order {// 订单数量private final List<Menu> orderList;public Order() {this.orderList = new ArrayList<Menu>();}// 创建订单public void createOrder(Menu menu){orderList.add(menu);}// 获取订单public List<Menu> getOrderList(){return orderList;}@Getter @Setterstatic class Menu{private String menuName; // 菜品名称private Integer num; // 菜品数量}}
1.3.创建接收者
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.invoke.MethodHandles;
import java.util.List;/*** 命令接收人* @author 13723* @version 1.0* 2024/2/4 11:08*/
public class Cook {private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());public void makeMenu(List<Order.Menu> menuList){logger.info("-------------------------> 厨师准备开始做菜 <-------------------------");for (Order.Menu menu : menuList) {logger.info("厨师正在做菜:{},数量:{}",menu.getMenuName(),menu.getNum());}logger.info("-------------------------> 厨师准备结束做菜 <-------------------------");}
}
1.4.创建具体命令
/*** 具体执行命令* @author 13723* @version 1.0* 2024/2/4 11:15*/
public class CookCommandImpl implements CookCommand{private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); // 厨师private Cook cook;// 订单private List<Order.Menu> orderList;CookCommandImpl(){}public CookCommandImpl(Cook cook,List<Order.Menu> orderList){this.cook = cook;this.orderList = orderList;}/*** 执行任务*/@Overridepublic void execute() {cook.makeMenu(orderList);}
}
1.5.创建调用者或者发送者
此处写在测试方法里了
/*** 命令模式 测试* @author 13723* @version 1.0* 2024/2/4 14:09*/
public class CommandTest {private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());@Test@DisplayName("测试命令模式-点餐,上菜")public void test1(){// 创建订单Order order = new Order();order.createOrder(new Order.Menu(){{setMenuName("土豆丝");setNum(1000);}});order.createOrder(new Order.Menu(){{setMenuName("拍黄瓜");setNum(5);}});order.createOrder(new Order.Menu(){{setMenuName("红烧肉");setNum(10);}});order.createOrder(new Order.Menu(){{setMenuName("黄焖鸡");setNum(30);}});// 发送命令到执行者CookCommandImpl cookCommand = new CookCommandImpl(new Cook(), order.getOrderList());// 执行命令cookCommand.execute();}
}
2.案例2-撤销订单(SpringBoot版本)
# 我们假设我们有一个简单的订单管理系统,用户可以执行添加订单、删除订单等操作,并且希望能够撤销(bug很多没考虑)之前的操作。
2.1.定义注解
import org.springframework.stereotype.Service;
import java.lang.annotation.*;@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Service
public @interface Command {/*** 标记具体事务的业务类型*/String type() default "";
}
2.2.定义订单命令接口
import com.hrfan.java_se_base.base_mvc.model.DecOrderHead;
/*** 定义订单命令接口*/
public interface OrderCommand {/*** 执行命令*/void execute(DecOrderHead order);/*** 撤销命令*/void undo(DecOrderHead order);
}
2.3.定义命令的具体实现类
import com.fasterxml.jackson.core.type.TypeReference;
import com.hrfan.java_se_base.base_mvc.mapper.DecOrderHeadMapper;
import com.hrfan.java_se_base.base_mvc.model.DecOrderHead;
import com.hrfan.java_se_base.base_mvc.service.DecOrderHeadService;
import com.hrfan.java_se_base.common.json.JsonObjectMapper;
import com.hrfan.java_se_base.pattern.commond_pattern.se.Order;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;import javax.annotation.Resource;
import java.lang.invoke.MethodHandles;
import java.util.Date;/*** 实现 添加订单命令 和 撤销订单命令 具体执行逻辑* @author 13723* @version 1.0* 2024/2/4 16:07*/
@Service
@Command(type = "insert")
public class InsertOrderCommand implements OrderCommand{private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());@Resourceprivate DecOrderHeadMapper mapper;@Overridepublic void execute(DecOrderHead order) {logger.error("----------------- 添加操作开始 -----------------");logger.error("添加用户");// 留存备份(上一步操作前数据记录)String json = JsonObjectMapper.getInstance().toJson(order);order.setBeforeInfo(json);int insert = mapper.insert(order);logger.error(insert > 0 ? "添加成功!":"添加失败!");}/*** 撤销操作(此撤销 仅撤销一次,真正撤销需要考虑是否撤销到第一次,例如增加计数,改变+1 撤销-1 到为 1时,此时不能在撤销)* @param order 订单信息*/@Overridepublic void undo(DecOrderHead order) {logger.error("----------------- 撤销操作开始 -----------------");DecOrderHead decOrderHead = mapper.selectById(order.getSid());Assert.notNull(decOrderHead,"订单未找到,撤销失败!");logger.error("撤销用户!");String beforeInfo = order.getBeforeInfo();DecOrderHead tempOrderHead = JsonObjectMapper.getInstance().fromJson(beforeInfo, new TypeReference<DecOrderHead>() {});tempOrderHead.setBeforeTime(new Date());tempOrderHead.setBeforeUser("System");mapper.deleteById(order);int insert = mapper.insert(tempOrderHead);logger.error(insert > 0 ? "撤销成功!":"撤销失败!");}
}
2.4.定义命令管理类
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.type.filter.AnnotationTypeFilter;/*** 定义命令管理器,用于将所有的使用注解的类注入到map中* 在使用的时候,直接从map中获取对应的命令,然后执行。*/
public class OrderCommandHistoryManager {/*** 存放 各种类型的具体命令* key :对应的业务乐星* value:对应的命令*/private Map<String,OrderCommand> handlerMap;public void setHandlerMap(List<OrderCommand> handlers) {handlerMap = scanHandlers(handlers);}/*** 扫描使用@Command注解的类 然后将其注入到map中* @param handlers 使用了@Command注解的类* @return 返回map集合*/private Map<String, OrderCommand> scanHandlers(List<OrderCommand> handlers) {ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);scanner.addIncludeFilter(new AnnotationTypeFilter(Command.class));Map<String, OrderCommand> handlerMap = new HashMap<>();handlers.forEach((it) -> {Class<?> aClass = it.getClass();if (hasDutyAnnotation(aClass)) {// 判断注解上的类型是否填写String type = getOrderAnnotationValue(aClass);// 将类型放到map中handlerMap.put(type, it);}});return handlerMap;}/*** 判断该类是否是 使用使用了Command注解* @param clazz 类名称* @return true使用了*/private boolean hasDutyAnnotation(Class<?> clazz ) {return AnnotationUtils.findAnnotation(clazz, Command.class) != null;}/*** 判断使用了的注解@Command 是否填写了type 类型* @param clazz 类名称* @return 返回类型*/private String getOrderAnnotationValue(Class<?> clazz) {Command command = AnnotationUtils.findAnnotation(clazz, Command.class);if (command == null) {throw new IllegalStateException("Adapter annotation not found for class: " + clazz);}return command.type();}/*** 获取map集合* @return 返回map集合*/public Map<String,OrderCommand> getHandlerMap() {return handlerMap;}}
2.5.定义配置类
/*** 命令管理类的配置类,用于将OrderCommandHistoryManager 注入到Spring的Bean中*/
@Configuration
public class OrderCommandConfiguration {@Beanpublic OrderCommandHistoryManager handlerOrderCommandExecute(List<OrderCommand> handlers) {// 创建对象 会获取全部使用注解的类,将其注入到对应map集合中OrderCommandHistoryManager manager = new OrderCommandHistoryManager();manager.setHandlerMap(handlers);return manager;}
}
2.6.定义公共调用服务
import com.hrfan.java_se_base.base_mvc.model.DecOrderHead;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import java.lang.invoke.MethodHandles;/*** 定义命令模式的公共服务(将所有的命令在此处进行处理)* 将此方法对外暴露,供其他模块调用,隐藏具体的命令实现,只暴露接口* @author 13723* @version 1.0* 2024/2/4 17:45*/
@Service
public class CommandCommonExecuteService {private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());@Autowiredprivate OrderCommandHistoryManager orderCommandHistoryManager;public void executeCommand(String type, DecOrderHead orderHead) {OrderCommand command = orderCommandHistoryManager.getHandlerMap().get(type);Assert.notNull(command,"no corresponding command found!");command.execute(orderHead);}public void undoLastCommand(String type,DecOrderHead orderHead) {OrderCommand command = orderCommandHistoryManager.getHandlerMap().get(type);Assert.notNull(command,"no corresponding command found!");command.undo(orderHead);}
}
2.7.测试订单管理
import com.hrfan.java_se_base.base_mvc.model.DecOrderHead;
import com.hrfan.java_se_base.common.json.JsonObjectMapper;
import com.hrfan.java_se_base.pattern.commond_pattern.boot.CommandCommonExecuteService;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.lang.invoke.MethodHandles;
import java.util.Date;
import java.util.UUID;/*** @author 13723* @version 1.0* 2024/2/5 07:09*/
@SpringBootTest
public class OrderCommandTest {private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());@Autowiredprivate CommandCommonExecuteService commandCommonService;@Test@DisplayName("测试命令模式")public void test(){logger.error("测试命令模式");DecOrderHead decOrderHead = new DecOrderHead() {{setSid(UUID.randomUUID().toString());setOrderNo("123456");setOrderPrice(100);setInsertTime(new Date());setInsertUser("Jack");}};// 添加订单前,将订单信息备份decOrderHead.setBeforeInfo(JsonObjectMapper.getInstance().toJson(decOrderHead));// 添加订单commandCommonService.executeCommand("insert",decOrderHead);// 撤销订单commandCommonService.undoLastCommand("insert",decOrderHead);}
}