责任链模式
责任链模式(Chain of Responsibility Pattern),顾名思义包含了链表的作用。因此要理解责任链,觉得在于两点:1、生成链的节点。2、如何链。
责任链的相关介绍就不多费口舌,可以点击查看相关引用 。
首先要创建一个抽象类。为啥呢?因为要做链式结构,而怎么与下一个节点关联就可以写到这个抽象父类的方法里,然后可以定义一个抽象方法给实现类,来实现具体的业务逻辑。说的好抽象,具体怎样弄还是直接上代码吧。
以打游戏为例,打过第一关才能进入第二关……第N关。
/**
* 游戏关卡
*/
public abstract class GameLevelHandler {
/**
* 下一个关卡
*/
protected GameLevelHandler gameLevelHandler;
/**
* 玩游戏
*/
public abstract void play();
/**
* 玩下一关
*/
protected void nextLevel() {
if (gameLevelHandler != null)
gameLevelHandler.play();// 玩下一关
}
public void setGameLevelHandler(GameLevelHandler gameLevelHandler) {
this.gameLevelHandler = gameLevelHandler;
}
}
实现类做如下处理,业务逻辑完成后调用下一关的方法。
/**
* 第一关
*/
public class FirstLevelHandler extends GameLevelHandler {
@Override
public void play() {
System.out.println("第一关 通关.......");
nextLevel();//去下一关
}
}
到此节点的生成也就差不多了。然后怎么形成链呢?咱继续……
二、如何去链
到此想一想也能明白了,最简单的方法不就是把每一关的对象都创建起来,然后把每关对应的下一关设置进去。
稍微优雅点,可以弄个工厂模式写好关联方案,只要玩第一关,后面的自然也就调用下去了。但感觉还是不够优雅啊,代码依然写的很死,如果以后关卡要换呢,那还得改代码。
可以把这个关联放入数据库中,做一个关联表,有当前关卡id,上一个关卡id,下一关关卡id这几个字段,那么上一个关卡id为空就代表第一关,下一关卡id为空就代表最后一关。这样就用数据库表模拟了一个链表的关联映射。
然后id的值用什么可以和关卡对象关联呢?如果项目中有spring那当然用spring的bean id即可。如果直接JavaSE写,那可以存储类路径,通过反射得到具体对象。
然后……,好抽象,还是直接上菜吧,这里就直接以数据库作链式结构来说。
如下建一个游戏关卡关联表,集成spring,关卡ID就是节点实现类的beanId。
CREATE TABLE `game_level` (
`ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`level_name` varchar(32) DEFAULT NULL COMMENT '关卡名称',
`level_id` varchar(32) DEFAULT NULL COMMENT '关卡主键id',
`prev_level_id` varchar(32) DEFAULT NULL COMMENT '上一个关卡',
`next_level_id` varchar(32) DEFAULT NULL COMMENT '下一个关卡',
PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8 COMMENT='游戏关卡关联表';
INSERT INTO `game_level` VALUES ('10', '打蚊子', 'firstLevelHandler', NULL, 'secondLevelHandler');
INSERT INTO `game_level` VALUES ('11', '打酱油', 'secondLevelHandler', 'firstLevelHandler', 'thirdLevelHandler');
INSERT INTO `game_level` VALUES ('12', '打怪兽', 'thirdLevelHandler', 'secondLevelHandler', NULL);
然后怎么取,怎么关联呢?继续看代码
/**
* 按数据库关联好链条,然后返回第一关
*
* @return
*/
public GameLevelHandler getFirstLevelHandler() {
// 1.从数据库中查询出第一关
GameLevelEntity firstEntity = gameLevelMapper.getFirstLevelHandler();
// 2.获取第一关的beanId,以此获得第一关对象
String levelId = firstEntity.getLevelId();
GameLevelHandler firstLevel = SpringUtils.getBean(levelId, GameLevelHandler.class);
// 3.下一关的ID
String nextId = firstEntity.getNextLevelId();
// 4. 设置一个关卡对象标识循环时的当前关卡,类似一个指针初始指向第一关卡
GameLevelHandler tempLevel = firstLevel;
while (!StringUtils.isEmpty(nextId)) {//如果下一关ID不为空则一直取
// 5.获得下一关卡设置到当前关卡中
GameLevelHandler nextLevel = SpringUtils.getBean(nextId, GameLevelHandler.class);
tempLevel.setNextLevelHandler(nextLevel);
// 6.从数据库取出下一关卡的关联信息,看是否还有再下一关
GameLevelEntity nextEntity = gameLevelMapper.getLevelByID(nextId);
if (nextEntity == null) {//如果没有再下一关循环结束
break;
}
// 7、如果还有再下一关,赋值给缓存对象继续循环
nextId = nextEntity.getNextLevelId();
tempLevel = nextLevel;
}
return firstLevel;
}
怎么取数据库的代码,在此担心篇幅太长就不粘贴代码了,因为示例的数据库表很简单,通过判断上一关卡的ID为空就能找到第一关。核心的代码逻辑基本如上,扩展的话还可以加上缓存,修改逻辑减少查询数据库次数以增加性能。
三、优缺点
请求不一定能处理请求,最好提供默认处理,构造链的有效性
四、应用场景
1.多条件流程判断 权限控制
2.OA系统流程审批
3.Java过滤器的底层实现Filter,在Java过滤器中客户端发送请求到服务器端,过滤会经过参数过滤、session过滤、表单过滤、隐藏过滤、检测请求头过滤
全部评论