责任链模式

一、简介

责任链模式是一种设计模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。

二、应用实例

  1. 红楼梦中的"击鼓传花";
  2. Js 中的事件冒泡;
  3. Java web 中 Apache Tomcat 对 Encoding 的处理;
  4. Struts2 的拦截器,Jsp Servlet 的 Filter。

三、优点

  1. 降低耦合度。它将请求的发送者和接收者解耦;
  2. 简化了对象。使得对象不需要知道链的结构;
  3. 增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任;
  4. 增加新的请求处理类很方便

四、缺点

  1. 不能保证请求一定被接收;
  2. 系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用;
  3. 可能不容易观察运行时的特征,有碍于除错

五、使用场景

  1. 有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定;
  2. 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求;
  3. 可动态指定一组对象处理请求。

六、实例

给定一个输入值,根据输入值执行不同逻辑。

String input = "1";  
if ("1".equals(input)) {
//TODO do something
} else if ("2".equals(input)) {
//TODO do something
} else if ("3".equals(input)) {
//TODO do something
}

或者:

String input = "1";   
switch (input) {
case "1":
//TODO do something
break;
case "2":
//TODO do something
break;
case "3":
//TODO do something
break;
default:
//TODO do something
break;
}

如何将多个if或者多个case拆分,就需要设计责任链模式。

  1. 定义一个抽象类
public abstract class BaseCase { 
// 为 true 表明自己可以处理该 case
private boolean isConsume;
public BaseCase(boolean isConsume) {
this.isConsume = isConsume;
}
// 下一个责任节点
private BaseCase nextCase;
public void setNextCase(BaseCase nextCase) {
this.nextCase = nextCase;
}
public void handleRequest() {
if (isConsume) {
// 如果当前节点可以处理,直接处理
doSomething();
} else {
// 如果当前节点不能处理,并且有下个节点,交由下个节点处理
if (null != nextCase) {
nextCase.handleRequest();
}
}
}
abstract protected void doSomething();
}
  1. 各个 case 来实现该抽象类
public class OneCase extends BaseCase { 
public OneCase(boolean isConsume) {
super(isConsume);
}

@Override protected void doSomething() {
// TODO do something
System.out.println(getClass().getName());
}
}
  1. 初始化各个 case,并指定每个 case 的下一个节点
String input = "1";      
OneCase oneCase = new OneCase("1".equals(input));
TwoCase twoCase = new TwoCase("2".equals(input));
DefaultCase defaultCase = new DefaultCase(true);
oneCase.setNextCase(twoCase);
twoCase.setNextCase(defaultCase);
oneCase.handleRequest();

七、进阶实例

可以将多个责任节点放到一个List中,然后比遍历处理。

场景 : 将一个字符串去头去尾去子串然后封装为一个map

  1. 定义一个责任接口类
import java.util.Map;

/**
* @author molong
*/
public interface ResolveChain {
/**
* 数据处理接口方法
*
* @param map
* @param input
* @param resolveChain
* @throws DataFormException
*/
void doSomething(Map<String, String> map, String input, ResolveChain resolveChain) throws DataFormException;
}
  1. 定义一个责任类的管理类
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* 处理socket传入得数据字符串
*
* @author molong
*
*/
public class MessageChain implements ResolveChain {

private List<ResolveChain> chainList = new ArrayList<>();

private int index = 0;

public MessageChain addResolveChain(ResolveChain resolveChain) {
chainList.add(resolveChain);
return this;
}

@Override
public void doSomething(Map<String, String> map, String input, ResolveChain resolveChain) throws DataFormException {
if (index == chainList.size()) { return ; }
ResolveChain resolve = chainList.get(index);
index ++;
resolve.doSomething(map, input, this);
}

/**
* 提供对外访问的方法
*
* @author molong
*
*/
public static Map<String, String> out(String str) throws DataFormException {
Map<String, String> map = new HashMap<>();
MessageChain messageChain = new MessageChain();
messageChain.addResolveChain(new HeadChain()).addResolveChain(new EquiChain()).addResolveChain(new DateChain());
messageChain.doSomething(map, str, messageChain);
return map;
}
}
  1. 实现接口类的实现方法
import org.apache.commons.lang3.StringUtils;

import java.util.Map;

/**
* 处理头字符数据
*
* @author molong
*/
public class HeadChain implements ResolveChain{

@Override
public void doSomething(Map<String, String> map, String input, ResolveChain resolveChain) throws DataFormException {
if(input.length() == FormatEnum.ALL.getSize()) {
if (StringUtils.equalsIgnoreCase(StringUtils.substring(input, 0, FormatEnum.START.getSize()), FormatEnum.START.getStr())
&& StringUtils.equalsIgnoreCase(StringUtils.substring(input, -(FormatEnum.END.getSize())), FormatEnum.END.getStr())) {
String newStr = input.substring(FormatEnum.START.getSize(), FormatEnum.ALL.getSize() - FormatEnum.END.getSize());
resolveChain.doSomething(map, newStr, resolveChain);
} else {
throw new DataFormException("数据头尾未包含2A/2B");
}
} else {
throw new DataFormException("数据格式错误");
}
}
}
import java.util.Map;

/**
* 处理设备信息
*
* @author molong
*/
public class EquiChain implements ResolveChain {


@Override
public void doSomething(Map<String, String> map, String input, ResolveChain resolveChain) throws DataFormException {
String equi = input.substring(0, FormatEnum.EQUI.getSize());
String id = input.substring(FormatEnum.EQUI.getSize(), FormatEnum.EQUI.getSize() + FormatEnum.ID.getSize());
if (StringUtils.isNotBlank(equi) && StringUtils.isNotBlank(id)) {
map.put("env_imeiid", equi + id);
String newStr = input.substring(FormatEnum.EQUI.getSize() + FormatEnum.ID.getSize());
resolveChain.doSomething(map, newStr, resolveChain);
} else {
throw new DataFormException("数据中未包含设备信息");
}
}
}
import java.util.Map;

/**
* 数据处理
*
* @author molong
*/
public class DateChain implements ResolveChain {

@Override
public void doSomething(Map<String, String> map, String input, ResolveChain resolveChain) throws DataFormException {
if (input.length() == FormatEnum.CONTENT.getSize() + FormatEnum.DATE.getSize()) {
String newStr = input.substring(FormatEnum.CONTENT.getSize());
// 电量
String electric = newStr.substring(0, FormatEnum.ELECTRIC.getSize());
String sun = newStr.substring(FormatEnum.ELECTRIC.getSize(), FormatEnum.ELECTRIC.getSize() + FormatEnum.SUN.getSize());
String temperature = StringUtils.substring(newStr, -(FormatEnum.HUMIDITY.getSize() + FormatEnum.TEMPERATURE.getSize()), -(FormatEnum.HUMIDITY.getSize()));
String humidity = StringUtils.substring(newStr, -(FormatEnum.HUMIDITY.getSize()));
if (StringUtils.isNotBlank(electric) && StringUtils.isNotBlank(sun) && StringUtils.isNotBlank(temperature) && StringUtils.isNotBlank(humidity)) {
map.put("env_electric", electric);
map.put("env_photometric", sun);
map.put("env_temperature", temperature);
map.put("env_humidity", humidity);
resolveChain.doSomething(map, null, resolveChain);
} else {
throw new DataFormException("数据出错");
}
} else {
throw new DataFormException("数据中未包含设备信息");
}
}
}
参考:​​https://www.jianshu.com/p/75946acd80e3​​​ 、​