责任链模式

情景:晚上去上英语课,为了好开溜坐到了最后一排。哇,前面坐了好几个漂亮的MM。哎,找张纸条,写上“Hi,可以做我的女朋友吗?如果不愿意请往前传”,纸条就一个接一个的传上去了。糟糕,传到第一排的MM把纸条传给老师了,听说是个老处女呀,快跑!

定义:在责任链模式中,很多对象由每一个对象对其下家的引用而接起来形成一条链。请求在这条链上传递,直到链上的某一个对象决定处理此请求。客户并不知道链上的哪一个最终处理这个请求,系统可以在不影响客户端的情况下动态的重新组织链和分配责任。处理者有两个选择:承担责任或者把责任推给下家。一个请求可以最终不被任何接收端对象所接受。

当你想要让一个以上的对象有机会能够处理某个请求的时候,就使用责任链模式。

类图

典型案例:Java Web的Filter、Spring MVC的DispatcherServlet、Spring Security的各种过滤器、Easy Rules...

下面,我们将分别模仿Java Web的Filter和Easy Rules,使用责任链模式实现跟它们类似的功能

一、FilterChain

在该例子中,按顺序调用一条链上的每一个对象,执行完后由该对象决定是否传递到下一个对象。

1、定义一个过滤链类,泛型表示传递的数据对象的类型。类里面定义了这条链上的过滤器集合,并提供增加的方法;还定义了线程独享的下一个要执行的过滤器所在集合位置变量。核心的是doFilter方法,先获取下一个要执行的过滤器所在集合位置,当过滤器集合里面存在该位置对应的过滤器时,就获取过滤器并执行它的doFilter方法,并将下一个要执行的过滤器所在集合位置指向下一个过滤器。

/**
 * 过滤链
 * @author z_hh
 * @time 2018年11月11日
 */
public class FilterChain<T> {
	
	/**
	 * 过滤器集合
	 */
	private List<Filter<T>> filters = new ArrayList<>();
	
	/**
	 * 线程隔离的下一个要执行的过滤器所在集合位置
	 */
	private static final ThreadLocal<Integer> NEXT_FILTER_NO = new ThreadLocal<Integer>() {
		protected Integer initialValue() {
	        return 0;
	    }
	};
	
	/**
	 * 执行下个过滤器
	 * @param t
	 */
	public void doFilter(T t) {
		Integer no = NEXT_FILTER_NO.get();
		if (no < filters.size()) {
			NEXT_FILTER_NO.set(no + 1);
			filters.get(no).doFilter(t, this);
		}
	}
	
	/**
	 * 添加过滤器,支持链式调用
	 * @param filter
	 * @return 当前FilterChain
	 */
	public FilterChain<T> addFilter(Filter<T> filter) {
		this.filters.add(filter);
		return this;
	}
	
}

2、定义一个过滤器接口,泛型表示传递的数据对象的类型。提供了一个doFilter方法,参数是传递的数据对象和过滤链对象引用。

/**
 * 过滤器接口
 * @author z_hh
 * @time 2018年11月11日
 */
public interface Filter<T> {

	void doFilter(T t, FilterChain<T> filterChain);
}

两个实现类,分别用于判断用户是否已经登录和是否是管理员(具体逻辑不要太在意)。

/**
 * 登录过滤器
 * @author z_hh
 * @time 2018年11月11日
 */
public class LoginFilter implements Filter<User> {

	@Override
	public void doFilter(User user, FilterChain<User> filterChain) {
		
		if (user.isLogin()) {
			System.out.println("欢迎您!");
			filterChain.doFilter(user);
		}
		else {
			System.out.println("您尚未登录!");
		}
		
	}

}
/**
 * 角色过滤器
 * @author z_hh
 * @time 2018年11月11日
 */
public class RoleFilter implements Filter<User> {

	@Override
	public void doFilter(User user, FilterChain<User> filterChain) {
		
		if (user.isManager()) {
			System.out.println("管理员您好!");
			filterChain.doFilter(user);
		}
		else {
			System.out.println("您不是管理员!");
		}
		
	}

}

3、定义用户对象,为了测试方便,特意定义了是否登录isLogin和是否是管理员isManager两个变量。同样,不要太在意细节。

/**
 * 用户类
 * @author z_hh
 * @time 2018年11月11日
 */
public class User {
	
	private String name;
	
	private boolean isLogin;
	
	private boolean isManager;

	public User(String name, boolean isLogin, boolean isManager) {
		super();
		this.name = name;
		this.isLogin = isLogin;
		this.isManager = isManager;
	}

    // 省略getter和setter方法

4、编写测试代码。

首先创建了4个用户对象,他们分别有不同的是否登录和是否是管理员的属性值。然后分别创建登录过滤器和角色过滤器的实例。接着创建过滤链对象,并添加两个过滤器进去。最后,用不同线程(需要考虑到执行过程导致的过滤链的下一个要执行的过滤器所在集合位置相互影响的问题)对不同的用户执行过滤链。

public class Test {

	public static void main(String[] args) throws InterruptedException {
		User user1 = new User("zhh", false, false),
			user2 = new User("zhou", true, false),
			user3 = new User("hua", false, true),
			user4 = new User("z", true, true);
		
		Filter<User> loginFilter = new LoginFilter(),
			roleFilter = new RoleFilter();
		
		FilterChain<User> filterChain = new FilterChain<>();
		filterChain.addFilter(loginFilter).addFilter(roleFilter);
			
		new Thread(() -> filterChain.doFilter(user1)).start();
		Thread.sleep(500);
		System.out.println("-----------------");
		
		new Thread(() -> filterChain.doFilter(user2)).start();
		Thread.sleep(500);
		System.out.println("-----------------");
		
		new Thread(() -> filterChain.doFilter(user3)).start();
		Thread.sleep(500);
		System.out.println("-----------------");
		
		new Thread(() -> filterChain.doFilter(user4)).start();
				
	}

}

5、结果,very perfect!

 

二、EasyRule

想要了解EasyRules,请参考https://github.com/j-easy/easy-rules

在该例子中,根据规则的优先级,会依次执行完所有规则。

1、定义规则的抽象父类,实现Comparable接口,以便在集合中可根据规则的优先级排序。两个抽象方法,当执行when方法返回true时,才会执行then的代码,参数map存储用户传进来的键值对引用。

/**
 * 规则抽象父类,实现Comparable接口,集合中对优先级排序
 * @author z_hh
 * @time 2018年11月11日
 */
public abstract class Rule implements Comparable<Rule> {

	/**
	 * 执行条件
	 * @param map 存放键值对
	 * @return true or false
	 */
	abstract boolean when(Map<String, Object> map);
	
	/**
	 * 逻辑处理
	 * @param map 存放键值对
	 */
	abstract void then(Map<String, Object> map);
	
	/**
	 * 优先级,默认100
	 * @return 优先级数值,越小先执行
	 */
	protected Integer priority() {
		return 100;
	}
	
	@Override
	public int compareTo(Rule r) {
		return this.priority().compareTo(r.priority());
	}
	
}

2、定义规则执行器,两个成员变量和对应的操作接口,rules变量用来存储要执行的规则,map变量用来存储用户传进来的键值对。还定义了run方法,用来遍历所有规则,当执行规则的when方法返回true时,就执行该规则的then方法。

/**
 * 规则执行器
 * @author z_hh
 * @time 2018年11月11日
 */
public class RuleExecutor {
	
	// 规则集合
	private List<Rule> rules = new ArrayList<>();
	
	// 参数map
	private Map<String, Object> map = new HashMap<>();
	
	// 私有化构造器
	private RuleExecutor() {}
	
	/**
	 * 创建一个规则执行器
	 * @return RuleExecutor
	 */
	public static RuleExecutor create() {
		return new RuleExecutor();
	}
	
	/**
	 * 添加单个键值
	 * @param key
	 * @param value
	 * @return 当前RuleExecutor
	 */
	public RuleExecutor addParam(String key, Object value) {
		map.put(key, value);
		return this;
	}
	
	/**
	 * 批量添加键值
	 * @param key
	 * @param value
	 * @return 当前RuleExecutor
	 */
	public RuleExecutor addParams(Map<String, Object> params) {
		this.map.putAll(map);
		return this;
	}
	
	/**
	 * 增加规则
	 * @param rules
	 * @return 当前RuleExecutor
	 */
	public RuleExecutor addRule(Rule... rules) {
		this.rules.addAll(Arrays.asList(rules));
		Collections.sort(this.rules);
		return this;
	}
	
	/**
	 * 执行
	 */
	public void run() {
		for (Rule rule : rules) {
			if (rule.when(map)) {
				System.out.println("rule " + rule.getClass().getSimpleName() + " trigger...");
				rule.then(map);
			}
		}
	}
	
	/**
	 * 根据键获取值
	 * @param key
	 * @return value
	 */
	public Object getParam(String key) {
		return map.get(key);
	}
	
	/**
	 * 获取所有键值对
	 * @return map
	 */
	public Map<String, Object> getParams() {
		return map;
	}
	
}

3、下面我们用这个工具来处理这样的一段逻辑:当上级分配一个任务时,根据其类型(需求、开发、测试)分配给不同的人员处理;我们使用三个规则,分别对这些任务做出响应。

首先定义一个任务类及任务类型的枚举:

/**
 * 任务类
 * @author z_hh
 * @time 2018年11月11日
 */
public class Task {

	/** 任务类型 */
	private TaskType taskType;
	
	/** 工作内容 */
	private String workContent;

	public Task(TaskType taskType, String workContent) {
		super();
		this.taskType = taskType;
		this.workContent = workContent;
	}

    // 省略getter和setter方法
/**
 * 任务类型枚举
 * @author z_hh
 * @time 2018年11月11日
 */
public enum TaskType {
	DEMAND,
	DEVELOP,
	TEST
}

接着定义三个规则实现类:

(1)需求规则,当任务类型是“需求”时,做出响应,并设置优先级的值为2。

/**
 * 需求规则
 * @author z_hh
 * @time 2018年11月11日
 */
public class DemanderRule extends Rule {

	@Override
	boolean when(Map<String, Object> map) {
		Task task = (Task) map.get("task");
		return Objects.nonNull(task) && Objects.equals(task.getTaskType(), TaskType.DEMAND);
	}

	@Override
	void then(Map<String, Object> map) {
		Task task = (Task) map.get("task");
		System.out.println("-->" + task.getWorkContent());
		System.out.println("-->有新需求了?我马上进行详细调研!");
	}
	
	@Override
	protected Integer priority() {
		return 2;
	}

}

(2)开发规则,当任务类型是“开发”时,做出响应,并设置优先级的值为1。

/**
 * 开发规则
 * @author z_hh
 * @time 2018年11月11日
 */
public class DeveloperRule extends Rule {

	@Override
	boolean when(Map<String, Object> map) {
		Task task = (Task) map.get("task");
		return Objects.nonNull(task) && Objects.equals(task.getTaskType(), TaskType.DEVELOP);
	}

	@Override
	void then(Map<String, Object> map) {
		Task task = (Task) map.get("task");
		System.out.println("-->" + task.getWorkContent());
		System.out.println("-->需求确定了?我马上设计系统框架!");
	}
	
	@Override
	protected Integer priority() {
		return 1;
	}

}

(3)测试规则,当任务类型是“测试”时,做出响应,使用默认优先级。 

/**
 * 测试规则
 * @author z_hh
 * @time 2018年11月11日
 */
public class TesterRule extends Rule {

	@Override
	boolean when(Map<String, Object> map) {
		Task task = (Task) map.get("task");
		return Objects.nonNull(task) && Objects.equals(task.getTaskType(), TaskType.TEST);
	}

	@Override
	void then(Map<String, Object> map) {
		Task task = (Task) map.get("task");
		System.out.println("-->这次的任务是:" + task.getWorkContent());
		System.out.println("-->功能做好了?我马上测试一下!");
	}
	
}

4、编写测试代码。

定义三个任务,类型分别是需求、开发、测试,然后分别对这些任务执行规则。

public class Test {

	public static void main(String[] args) {
		Task demand = new Task(TaskType.DEMAND, "客户又又又提新的需求了!");
		Task develop = new Task(TaskType.DEVELOP, "需求定下来了,应该可能大概不会再改了!");
		Task test = new Task(TaskType.TEST, "功能做好了,我觉得不会有bug!");
		
		// 当任务是需求调研时
		RuleExecutor executor1 = RuleExecutor.create()
				.addRule(new DemanderRule(), new DeveloperRule(), new TesterRule())
				.addParam("task", demand);
		executor1.run();
		
		System.out.println("-----------------完美分割线-----------------");
		
		// 当任务是功能开发时
		RuleExecutor executor2 = RuleExecutor.create()
				.addRule(new DeveloperRule())
				.addRule(new DemanderRule())
				.addRule(new TesterRule())
				.addParam("task", develop);
		executor2.run();
		
		System.out.println("-----------------完美分割线-----------------");
		
		// 当任务是测试时
		Rule[] rules = new Rule[] {new DemanderRule(), new DeveloperRule(), new TesterRule()};
		RuleExecutor executor3 = RuleExecutor.create()
				.addRule(rules)
				.addParam("task", test);
		executor3.run();

	}

}

 5、运行及结果

本文内容到此结束了,有什么问题或者建议,欢迎在评论区进行探讨! 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值