设计模式——责任链模式(Chain of Responsibility Pattern)
这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这是一个简单的责任链模式,作用也体现出来了,作为果农(请求方),我们不需要知道我们的果子被谁收购了(不需要知道我们到底要调用哪个接口),只要把果子拿出来交给责任链,他就会帮助我们找到收购方(只要交给服务器,服务器就会帮我们找到要调用的接口)。秋天来了,果农伯伯种的果子都成熟了,需要把果子卖出去,果子有好有坏,品质大不相同,所以买的方式也不一样
定义:这种类型的设计模式属于行为型模式。责任链模式为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
责任链模式也是我在看Tomcat源码的时候了解到的一种设计模式,Tomcat是用它来制作过滤器的。我想了想,责任链模式可以用如下例子来通俗的讲解。
秋天来了,果农伯伯种的果子都成熟了,需要把果子卖出去,果子有好有坏,品质大不相同,所以买的方式也不一样,好的果子可能就被作为新鲜水果出售,差一点的果子可能就被作为罐头、果汁等其他方式进行销售,如下图。
我们看这个图,如果有学习过链表的小伙伴就不难发现,这个结构就是一个链表嘛,所以才叫责任链嘛。那这个责任链在Java语言中应该如何使用呢?下面就是这个模式的Java流程。
1.抽象商店,利用了多态的概念
/**
* @description: 抽象商店,用于规范各个商店的逻辑及提取公共内容
* @author: Me
* @createDate: 2022/10/23 15:34
* @version: 1.0
*/
public abstract class AbstractStore {
// 水果的重量
protected int weight;
//责任链中的下一个采购方
protected AbstractStore nextStore;
/**
* 确定下一个采购方对象
* @param nextStore 下一个采购方对象
*/
public void setNextStore(AbstractStore nextStore){
this.nextStore = nextStore;
}
/**
* 检查产品是否符合要求,不符合则跳过此采购方,进入下一个采购方的筛选
* @param weight 水果的重量
*/
public void examine(int weight){
// 如果水果的重量大于采购方采购水果的最少重量,则被采购
if(weight >= this.weight){
buy(weight);
// 采购结束返回
return;
}
// 如果没有被采购,且下一个采购方不为空,则被下一个采购方采购
if(nextStore !=null){
// 下一个采购方的检查产品方法
nextStore.examine(weight);
}
}
/**
* 符合则发出提示,此商品被收购了
* @param weight 水果的重量
*/
abstract protected void buy(int weight);
}
2.各个继承的子类(三个)
/**
* @description: 水果商店
* @author: Me
* @createDate: 2022/10/23 15:34
* @version: 1.0
*/
public class FruitStore extends AbstractStore {
public FruitStore(int weight){
this.weight = weight;
}
@Override
protected void buy(int weight) {
System.out.println("有一个重" + weight + "g的苹果被水果商店收购了");
}
}
/**
* @description: 水果罐头商店
* @author: Me
* @createDate: 2022/10/23 15:34
* @version: 1.0
*/
public class CannedFruitStore extends AbstractStore {
public CannedFruitStore(int weight){
this.weight = weight;
}
@Override
protected void buy(int weight) {
System.out.println("有一个重" + weight + "g的苹果被水果罐头商店收购了");
}
}
/**
* @description: 果汁商店
* @author: Me
* @createDate: 2022/10/23 15:34
* @version: 1.0
*/
public class FruitJuiceStore extends AbstractStore {
public FruitJuiceStore(int weight){
this.weight = weight;
}
@Override
protected void buy(int weight) {
System.out.println("有一个重" + weight + "g的苹果被果汁商店收购了");
}
}
3.业务逻辑类
/**
* @description: 业务逻辑类
* @author: Me
* @createDate: 2022/10/23 15:34
* @version: 1.0
*/
public class ChainPatternDemo {
// 大于250g/个的苹果被作为水果被水果店采购
public static int FRUIT_WEIGHT = 250;
// 大于150g/个且小于250g/个的苹果作为水果罐头被罐头工厂采购
public static int CANNED_FRUIT_WEIGHT = 150;
// 小于150g/个的苹果作为果汁被果汁工厂采购
public static int FRUIT_JUICE_WEIGHT = 0;
// 通过静态方法去获取抽象的商店类
private static AbstractStore getChainOfStore(){
// 通过指定重量去规定水果应该谁来采购
AbstractStore cannedFruitStore = new CannedFruitStore(CANNED_FRUIT_WEIGHT);
AbstractStore fruitJuiceStore = new FruitJuiceStore(FRUIT_JUICE_WEIGHT);
AbstractStore fruitStore = new FruitStore(FRUIT_WEIGHT);
// 设置责任链的下一个采购方,确定整个链表的顺序
fruitStore.setNextStore(cannedFruitStore);
cannedFruitStore.setNextStore(fruitJuiceStore);
// 最后将链表的首位元素进行返回
return fruitStore;
}
public static void main(String[] args) {
// 获取整个责任链对象
AbstractStore loggerChain = getChainOfStore();
// 调用责任链中检查水果的方法,输入水果数量进行检查,返回被收购方的信息
// 大于250g被水果商店收购
loggerChain.examine(300);
// 大于150g被水果罐头商店收购
loggerChain.examine(180);
// 小于150g被果汁商店收购
loggerChain.examine(80);
}
}
最后方法执行的结果为:
这是一个简单的责任链模式,作用也体现出来了,作为果农(请求方),我们不需要知道我们的果子被谁收购了(不需要知道我们到底要调用哪个接口),只要把果子拿出来交给责任链,他就会帮助我们找到收购方(只要交给服务器,服务器就会帮我们找到要调用的接口)。
优点:
1、降低耦合度。它将请求的发送者和接收者解耦。
2、简化了对象。使得对象不需要知道链的结构。
3、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。
4、增加新的请求处理类很方便。
缺点:
1、不能保证请求一定被接收。
2、系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。
3、可能不容易观察运行时的特征,有碍于除错。
注意:
这种模式个人认为需要较高的功力或者经验才能够使用得当,因为需要照顾的点比较多,比如简便的增加链或删除链,请求的被链拦截或者抛弃的返回信息,责任链中判断条件的设置等等。
本文章也是总结菜鸟教程中对于责任链的讲解,加了自己比较简单通俗的理解,有需要的小伙伴可以在菜鸟教程中再看一下责任链模式。
(如下内容非必要,请选择性观看)这个模式在Tomcat中也有涉及,是用来作为过滤器使用的。如果不了解的小伙伴可以把它想成一个在执行业务逻辑controller之前做的一个请求校验,只有这个请求校验过了之后才会处理这个请求。
但是在Tomcat中的责任链模式则与上述例子不太相同。我们来一起看看吧。
这段代码就是去创建过滤器链。下面让我们看看他是如何创建的
/**
* Construct a FilterChain implementation that will wrap the execution of
* the specified servlet instance.
*
* @param request The servlet request we are processing
* @param wrapper The wrapper managing the servlet instance
* @param servlet The servlet instance to be wrapped
*
* @return The configured FilterChain instance or null if none is to be
* executed.
*
* 创建过滤器的方法
*/
public static ApplicationFilterChain createFilterChain(ServletRequest request,
Wrapper wrapper, Servlet servlet) {
// If there is no servlet to execute, return null
if (servlet == null)
return null;
// 下面就是一些校验,去获取过滤器链或者去创建一个新的过滤器链
// Create and initialize a filter chain object
ApplicationFilterChain filterChain = null;
if (request instanceof Request) {
Request req = (Request) request;
if (Globals.IS_SECURITY_ENABLED) {
// Security: Do not recycle
filterChain = new ApplicationFilterChain();
} else {
filterChain = (ApplicationFilterChain) req.getFilterChain();
if (filterChain == null) {
filterChain = new ApplicationFilterChain();
req.setFilterChain(filterChain);
}
}
} else {
// Request dispatcher in use
filterChain = new ApplicationFilterChain();
}
filterChain.setServlet(servlet);
filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());
// Acquire the filter mappings for this Context
// 去查询过滤器链如果没有过滤器链则直接返回
StandardContext context = (StandardContext) wrapper.getParent();
FilterMap filterMaps[] = context.findFilterMaps();
// If there are no filter mappings, we are done
if ((filterMaps == null) || (filterMaps.length == 0))
return filterChain;
// Acquire the information we will need to match filter mappings
DispatcherType dispatcher =
(DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);
String requestPath = null;
Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
if (attribute != null){
requestPath = attribute.toString();
}
String servletName = wrapper.getName();
// Add the relevant path-mapped filters to this filter chain
// 找到过滤器链后进行循环,将相关的路径映射过滤器添加到过滤器链中
for (int i = 0; i < filterMaps.length; i++) {
if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
continue;
}
if (!matchFiltersURL(filterMaps[i], requestPath))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
if (filterConfig == null) {
// FIXME - log configuration problem
continue;
}
filterChain.addFilter(filterConfig);
}
// Add filters that match on servlet name second
// 循环过滤器,与servletName匹配的过滤器也添加到过滤器链中
for (int i = 0; i < filterMaps.length; i++) {
if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
continue;
}
if (!matchFiltersServlet(filterMaps[i], servletName))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
if (filterConfig == null) {
// FIXME - log configuration problem
continue;
}
filterChain.addFilter(filterConfig);
}
// Return the completed filter chain
// 返回过滤器链
return filterChain;
}
这个方法是针对于请求的,每个请求都会来调用这个方法。
与我们的责任链例子不同的是,Tomcat的责任链是使用数组进行保存的。
每个过滤器都会使用addFilter()方法添加到该数组中,并没有使用链表模式。说明过滤器链使用数组模式会更好,那么数组与链表相比优点在哪里呢?无非就是按下标取值比较快,不用每次都遍历整个集合。所以说明Tomcat中的过滤器拥有读多写少,需要获取指定位置过滤器的要求。这个例子也告诉我们设计模式只是一个理念,具体使用方法的选择我们就需要根据实际应用的场景来选择了。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)