責任鏈模式是一種設(shè)計模式悴灵。在責任鏈模式里,很多對象由每一個對象對其下家的引用而連接起來形成一條鏈。請求在這個鏈上傳遞淹禾,直到鏈上的某一個對象決定處理此請求。發(fā)出這個請求的客戶端并不知道鏈上的哪一個對象最終處理這個請求茴扁,這使得系統(tǒng)可以在不影響客戶端的情況下動態(tài)地重新組織和分配責任铃岔。
比如,一個請假申請就可以很好的描述這種模式:

在“iluwatar/java-design-patterns”上有一個很好的例子:
責任鏈模式
在這個例子中峭火,我們來看看是怎么傳遞責任的毁习。
首先是責任鏈條的某一個鏈:
public class OrcOfficer extends RequestHandler {
public OrcOfficer(RequestHandler handler) {
super(handler);
}
@Override
public void handleRequest(Request req) {
if (req.getRequestType().equals(RequestType.TORTURE_PRISONER)) {
printHandling(req);
req.markHandled();
} else {
super.handleRequest(req);
}
}
@Override
public String toString() {
return "Orc officer";
}
}
在這個類里,如果要初始化這個類卖丸,必須在構(gòu)造器里給定一個下家public OrcOfficer(RequestHandler handler)
纺且,這就形成了一個鏈條了。
其次稍浆,在處理請求的時候载碌,首先是做判斷,如果該它處理的衅枫,就處理了嫁艇,責任鏈的傳遞也就結(jié)束了;如果不該它處理弦撩,則交由父類的方法處理步咪。
那么,我們來看看益楼,父類的handleRequest
方法里干了什么呢歧斟?
public void handleRequest(Request req) {
if (next != null) {
next.handleRequest(req);
}
}
可以看到,該方法首先是問還有沒有下家偏形,如果有静袖,則交給下家處理;如果沒有俊扭,則結(jié)束队橙。
完整的例子,請參考責任鏈模式萨惑。
責任鏈模式解決問題的思想是十分優(yōu)雅的捐康,有很好的擴展性,把一個復雜的問題做了簡單的分解庸蔼,使得我們實現(xiàn)起來相當?shù)暮唵巍?br>
不好的地方解总,是面向?qū)ο笳Z言的弱點,必須實現(xiàn)很多的子類來做責任鏈姐仅,造成了類的眾多和代碼的重復花枫。比如上述例子中刻盐,就有OrcCommander
、OrcOfficer
和OrcSoldier
三個子類劳翰,這些子類都存在不同程度的代碼重復敦锌。
我們現(xiàn)在來用函數(shù)式編程來解決面向?qū)ο蟮娜觞c。
首先佳簸,RequestType
和Request
可以保留:
public enum RequestType {
DEFEND_CASTLE, TORTURE_PRISONER, COLLECT_TAX
}
public class Request {
private final RequestType requestType;
private final String requestDescription;
private boolean handled;
public Request(final RequestType requestType, final String requestDescription) {
this.requestType = Objects.requireNonNull(requestType);
this.requestDescription = Objects.requireNonNull(requestDescription);
}
public String getRequestDescription() {
return requestDescription;
}
public RequestType getRequestType() {
return requestType;
}
public void markHandled() {
this.handled = true;
}
public boolean isHandled() {
return this.handled;
}
@Override
public String toString() {
return getRequestDescription();
}
}
RequestHandler類需要做相當?shù)母脑欤?/p>
public class RequestHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(RequestHandler.class);
private RequestHandler next = null;
public void setNext(RequestHandler next) {
this.next = next;
}
上面是下家的代碼和設(shè)置下家的代碼乙墙。
private Predicate<Request> predicate;
public void withPredication(Predicate<Request> predicate)
{
this.predicate = predicate;
}
判斷條件,判斷是否該本節(jié)點處理生均。
private Consumer<Request> action;
public void withAction(Consumer<Request> action)
{
this.action = action;
}
處理方法听想。
最終對請求的處理:
public void handleRequest(Request req) throws Exception{
if (this.predicate == null || this.action == null) throw new Exception("必須先通過withPredication和withAction給定判定條件和執(zhí)行方法!");
if (this.predicate.test(req)) this.action.accept(req);
如果該本節(jié)點處理马胧,則處理哗魂。否則:
else if (next != null) {
next.handleRequest(req);
}
}
扔給下家處理。
下面漓雅,我們來看看OrcKing
類如何構(gòu)建責任鏈:
public class OrcKingKing {
private static final Logger LOGGER = LoggerFactory.getLogger(OrcKingKing.class);
RequestHandler chain;
public OrcKingKing() {
buildChain();
}
private void buildChain() {
RequestHandler orcCommander = new RequestHandler();
orcCommander.withPredication(comparedWith.apply(DEFEND_CASTLE));
orcCommander.withAction(actionWith.apply("OrcCommander"));
RequestHandler orcOfficer = new RequestHandler();
orcOfficer.withPredication(comparedWith.apply(TORTURE_PRISONER));
orcOfficer.withAction(actionWith.apply("OrcOffice"));
orcCommander.setNext(orcOfficer);
RequestHandler orcSoldier = new RequestHandler();
orcSoldier.withPredication(comparedWith.apply(COLLECT_TAX));
orcSoldier.withAction(actionWith.apply("OrcSoldier"));
orcOfficer.setNext(orcSoldier);
this.chain = orcCommander;
}
public void makeRequest(Request req) throws Exception{
chain.handleRequest(req);
}
private Function<RequestType, Predicate<Request>> comparedWith = type -> req -> req.getRequestType().equals(type);
private Function<String, Consumer<Request>> actionWith = objectType -> req -> {
LOGGER.info("{} handling request \"{}\"", objectType, req);
req.markHandled();
};
}
這樣录别,就完成了責任鏈的構(gòu)建。
在函數(shù)式編程里邻吞,函數(shù)是可以作為參數(shù)傳遞的组题。我們通過把方法傳遞給某一個對象,達到了多態(tài)的目的抱冷,就不需要做子類繼承了崔列。
RequestHandler orcCommander = new RequestHandler();
orcCommander是一個對象,通過下面的代碼傳遞了兩個運行時的函數(shù):
orcCommander.withPredication(comparedWith.apply(DEFEND_CASTLE));
orcCommander.withAction(actionWith.apply("OrcCommander"));
通過函數(shù)式編程旺遮,函數(shù)可以不斷的抽象赵讯,就像下面的代碼:
private Function<RequestType, Predicate<Request>> comparedWith = type -> req -> req.getRequestType().equals(type);
private Function<String, Consumer<Request>> actionWith = objectType -> req -> {
LOGGER.info("{} handling request \"{}\"", objectType, req);
req.markHandled();
};
通過這種方式傳遞了函數(shù),就避免了子類眾多的問題耿眉,因為在面向?qū)ο蟮恼Z言中边翼,函數(shù)必須依附在類身上,不能獨立存在鸣剪。而函數(shù)式編程中组底,函數(shù)是可以獨立存在的,它也可以像對象一樣筐骇,在運行時進行傳遞债鸡。
最后,我們就可以使用這個責任鏈了:
OrcKingKing king = new OrcKingKing();
king.makeRequest(new Request(RequestType.DEFEND_CASTLE, "defend castle"));
king.makeRequest(new Request(RequestType.TORTURE_PRISONER, "torture prisoner"));
king.makeRequest(new Request(RequestType.COLLECT_TAX, "collect tax"));
看看铛纬,函數(shù)式編程的思路厌均,是不是很有意思!
參考文獻:chain