天天看点

设计模式-职责链模式(上)

1、模板模式、策略模式、职责链模式。这三种模式具有相同的作用:复用和扩展,在实际的项目开发中比较常用,特别是框架开发中,我们可以利用它们来提供框架的扩展点,能够让框架的使用者在不修改框架源码的情况下,基于扩展点定制化框架的功能。

2、什么是指责链模式:

设计模式-职责链模式(上)

       翻译成中文就是:将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。将这些接收对象串成一条链,并沿着这条链传递这个请求,直到链上的某个接收对象能够处理它为止。3、职责链模式的模板代码:(这里既用到了模板模式也用到了职责链模式)

public abstract class Handler {
  protected Handler successor = null;

  public void setSuccessor(Handler successor) {
    this.successor = successor;
  }

  public final void handle() {
    boolean handled = doHandle();
    if (successor != null && !handled) {
      successor.handle();
    }
  }

  protected abstract boolean doHandle();
}

public class HandlerA extends Handler {
  @Override
  protected boolean doHandle() {
    boolean handled = false;
    //...
    return handled;
  }
}

public class HandlerB extends Handler {
  @Override
  protected boolean doHandle() {
    boolean handled = false;
    //...
    return handled;
  }
}


public class HandlerChain {
  private Handler head = null;
  private Handler tail = null;

  public void addHandler(Handler handler) {
    handler.setSuccessor(null);

    if (head == null) {
      head = handler;
      tail = handler;
      return;
    }

    tail.setSuccessor(handler);
    tail = handler;
  }

  public void handle() {
    if (head != null) {
      head.handle();
    }
  }
}

// 使用举例
public class Application {
  public static void main(String[] args) {
    HandlerChain chain = new HandlerChain();
    chain.addHandler(new HandlerA());
    chain.addHandler(new HandlerB());
    chain.handle();
  }
}
           

4、 第二种实现方式,代码如下所示。这种实现方式更加简单。HandlerChain 类用数组而非链表来保存所有的处理器,并且需要在 HandlerChain 的 handle() 函数中,依次调用每个处理器的 handle() 函数。

/**
 * 处理器接口
 * @author PengMvc
 *
 */
public interface Ihandle {
	public Boolean handle();
}

/**
 * Ahandle类
 * @author PengMvc
 *
 */
public class Ahandle implements Ihandle{

	@Override
	public Boolean handle() {
		System.out.println("我是A处理器");
		// 省略A处理器的具体处理逻辑
		return true;
	}

}

/**
 * Bhandle类
 * @author PengMvc
 *
 */
public class Bhandle implements Ihandle{

	@Override
	public Boolean handle() {
		System.out.println("我是B处理器");
		// 省略B处理器的具体处理逻辑
		return true;
	}

}

/**
 * 处理器链
 * 只要一个没有处理成功,那么整个处理器就没有成功
 * @author PengMvc
 *
 */
public class HandleChain {
	
	List<Ihandle> handleList=new ArrayList();

	// 注册处理器
	public void addHandle(Ihandle handle) {
		this.handleList.add(handle);
	}
    
	// 判断是否处理成功
	public Boolean doHandle() {
	  for(Ihandle handle :handleList) {
			if(!handle.handle()) {
				return false;
			}
		}
		return true;
	}
	
}

/**
 * 处理器链,无论如何所有的处理器都会执行一遍
 * 最后根据处理的结果,确定是否处理成功
 * @author PengMvc
 *
 */
public class HandleChain2 {
	
	List<Ihandle> handleList = new ArrayList();
	
	// 各个处理器的结果集合
	List<Boolean> ifSuccessList = new ArrayList();

	// 注册处理器
	public void addHandle(Ihandle handle) {
		this.handleList.add(handle);
	}
    
	// 判断是否处理成功
	public Boolean doHandle() {
		Boolean ifHandle =  true;
		for(Ihandle handle :handleList) {
			ifSuccessList.add(handle.handle());
		}
	List<Boolean> filterList=ifSuccessList.stream().
                    filter(flag->flag.equals(false)).collect(Collectors.toList());
	ifHandle = (filterList !=null && filterList.size()==0) ? true : false;
	    
	return ifHandle ;
	}
}

/**
 * 测试类
 * @author PengMvc
 *
 */
public class HandleTest {
	public static void main(String[] args) {
		HandleChain handleChain = new HandleChain();
		
		// handleChain注册需要处理的处理器
		handleChain.addHandle(new Ahandle());
		handleChain.addHandle(new Bhandle());
		
		// 进行处理
		String ifsuccess=handleChain.doHandle() ? "所用处理器处理成功":"处理失败";
		System.out.println(ifsuccess);
	}
}



           

5、职责链模式的应用场景:

利用职责链模式可以灵活扩展敏感词过滤框架:代码如下

public interface SensitiveWordFilter {
  boolean doFilter(Content content);
}

public class SexyWordFilter implements SensitiveWordFilter {
  @Override
  public boolean doFilter(Content content) {
    boolean legal = true;
    //...
    return legal;
  }
}

// PoliticalWordFilter、AdsWordFilter类代码结构与SexyWordFilter类似

public class SensitiveWordFilterChain {
  private List<SensitiveWordFilter> filters = new ArrayList<>();

  public void addFilter(SensitiveWordFilter filter) {
    this.filters.add(filter);
  }

  // return true if content doesn't contain sensitive words.
  public boolean filter(Content content) {
    for (SensitiveWordFilter filter : filters) {
      if (!filter.doFilter(content)) {
        return false;
      }
    }
    return true;
  }
}

public class ApplicationDemo {
  public static void main(String[] args) {
    SensitiveWordFilterChain filterChain = new SensitiveWordFilterChain();
    filterChain.addFilter(new AdsWordFilter());
    filterChain.addFilter(new SexyWordFilter());
    filterChain.addFilter(new PoliticalWordFilter());

    boolean legal = filterChain.filter(new Content());
    if (!legal) {
      // 不发表
    } else {
      // 发表
    }
  }
}
           

       看了上面的实现,你可能会说,我像下面这样也可以实现敏感词过滤功能,而且代码更加简单,为什么非要使用职责链模式呢?这是不是过度设计呢?以下是不是很好的实现:

public class SensitiveWordFilter {
  // return true if content doesn't contain sensitive words.
  public boolean filter(Content content) {
    if (!filterSexyWord(content)) {
      return false;
    }

    if (!filterAdsWord(content)) {
      return false;
    }

    if (!filterPoliticalWord(content)) {
      return false;
    }

    return true;
  }

  private boolean filterSexyWord(Content content) {
    //....
  }

  private boolean filterAdsWord(Content content) {
    //...
  }

  private boolean filterPoliticalWord(Content content) {
    //...
  }
}
           

5.1、使用职责链模式的好处:

5.1.1、首先,我们来看,职责链模式如何应对代码的复杂性。

      将大块代码逻辑拆分成函数,将大类拆分成小类,是应对代码复杂性的常用方法。应用职责链模式,我们把各个敏感词过滤函数继续拆分出来,设计成独立的类,进一步简化了 SensitiveWordFilter 类,让 SensitiveWordFilter 类的代码不会过多,过复杂。

5.1.2、我们再来看,职责链模式如何让代码满足开闭原则,提高代码的扩展性。

       当我们要扩展新的过滤算法的时候,比如,我们还需要过滤特殊符号,按照非职责链模式的代码实现方式,我们需要修改 SensitiveWordFilter 的代码,违反开闭原则。不过,这样的修改还算比较集中,也是可以接受的。而职责链模式的实现方式更加优雅,只需要新添加一个 Filter 类,并且通过 addFilter() 函数将它添加到 FilterChain 中即可,其他代码完全不需要修改。

       不过,你可能会说,即便使用职责链模式来实现,当添加新的过滤算法的时候,还是要修改客户端代码(ApplicationDemo),这样做也没有完全符合开闭原则。

       实际上,细化一下的话,我们可以把上面的代码分成两类:框架代码和客户端代码。其中,ApplicationDemo 属于客户端代码,也就是使用框架的代码。除 ApplicationDemo 之外的代码属于敏感词过滤框架代码。

       假设敏感词过滤框架并不是我们开发维护的,而是我们引入的一个第三方框架,我们要扩展一个新的过滤算法,不可能直接去修改框架的源码。这个时候,利用职责链模式就能达到开篇所说的,在不修改框架源码的情况下,基于职责链模式提供的扩展点,来扩展新的功能。换句话说,我们在框架这个代码范围内实现了开闭原则。

       除此之外,利用职责链模式相对于不用职责链的实现方式,还有一个好处,那就是配置过滤算法更加灵活,可以只选择使用某几个过滤算法,譬如我们可以通过properties配置我们的filter,我简单的实现如下可以参考,我们加了properties,很多项目都有配置中心,那么我们可以在配置中心里灵活的配置我们想要的filter,这样代码更加符合开闭原则,代码如下:

/**
 * handle客户端
 * @author PengMvc
 *
 */
public class HandleApplication {
	
	// autowire handle
    private static final HandleChain handleChain = new HandleChain();
    
    // when application started,all handles already load to chain.
    static {
    	FileInputStream fileInputStream;
		try {
			Properties properties = new Properties();
			fileInputStream = new FileInputStream(new File("src/resource/handlebean.properties"));
			properties.load(fileInputStream);
			Enumeration<Object> keys = properties.keys();
			while(keys.hasMoreElements()) {
				// key
				String key = keys.nextElement().toString();
				
				// get classPath
				String classPath = properties.getProperty(key);
				
				// get bean 
				Ihandle instance = (Ihandle) Class.forName(classPath).newInstance();
				
				// register handle
				handleChain.addHandle(instance);
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    }
	
	public static void main(String[] args) throws Exception {
		// to handle
		String ifsuccess=handleChain.doHandle() ? "所用处理器处理成功":"处理失败";
		System.out.println(ifsuccess);
	}
	
  
}
           

6、总结:

       在职责链模式中,多个处理器依次处理同一个请求。一个请求先经过 A 处理器处理,然后再把请求传递给 B 处理器,B 处理器处理完后再传递给 C 处理器,以此类推,形成一个链条。链条上的每个处理器各自承担各自的处理职责,所以叫作职责链模式。

       在 GoF 的定义中,一旦某个处理器能处理这个请求,就不会继续将请求传递给后续的处理器了。当然,在实际的开发中,也存在对这个模式的变体,那就是请求不会中途终止传递,而是会被所有的处理器都处理一遍。

       职责链模式有两种常用的实现。一种是使用链表来存储处理器,另一种是使用数组来存储处理器,后面一种实现方式更加简单。

继续阅读