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 的定義中,一旦某個處理器能處理這個請求,就不會繼續将請求傳遞給後續的處理器了。當然,在實際的開發中,也存在對這個模式的變體,那就是請求不會中途終止傳遞,而是會被所有的處理器都處理一遍。
職責鍊模式有兩種常用的實作。一種是使用連結清單來存儲處理器,另一種是使用數組來存儲處理器,後面一種實作方式更加簡單。