簡述
責(zé)任鏈模式是一種對象的行為模式。通過建立一條鏈來組織請求的處理者,請求將沿著鏈進(jìn)行傳遞雹锣,請求發(fā)送者無須知道請求在何時戈咳、何處以及如何被處理心软,實現(xiàn)了請求發(fā)送者與處理者的解耦。
如Android 中的事件傳遞著蛙,Activity->ViewGroup->View删铃,當(dāng)然也可以View->ViewGroup-> Activity ,android 中是U形事件傳遞
另外OKhttp的interceptors實現(xiàn)踏堡,OkHttp發(fā)送網(wǎng)絡(luò)請求的一切核心功能猎唁,包括建立連接、發(fā)送請求暂吉、讀取緩存等胖秒,都是通過interceptors來實現(xiàn)的。這些interceptors在運行的時候彼此協(xié)作慕的,構(gòu)成了一個責(zé)任鏈interceptor chain
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
在 OkHttp 中阎肝,命令對象就是 Request 對象,處理對象就是每一個 Interceptor 對象肮街。每個 interceptor 對 request 進(jìn)行一些步驟的處理风题,而將其余的工作交給下一個 interceptor。注意到,責(zé)任鏈中的處理對象如果可以全權(quán)處理命令對象沛硅,則不需要交給下一個處理對象眼刃。OkHttp 中的 CacheInterceptor 也是具有全權(quán)處理的能力。如果請求的結(jié)果已經(jīng)緩存摇肌,則不需要再交給 ConnectInterceptor 等進(jìn)行連接服務(wù)器擂红、發(fā)送請求的處理,直接返回已緩存的 response 即可围小。
例如常見的流程審批(當(dāng)然在企業(yè)級APP中由服務(wù)端處理)
fun main(args: Array<String>) {
// 引用鏈
val cto = CTO(null)
val department = DepartmentLeader(cto)
val group = GroupLeader(department)
//
group.handleEvent(FlowEvent("filepath ", 113))
}
data class FlowEvent(val filePath: String, val fileID: Int)
interface FlowHandler {
val next: FlowHandler?
fun handleEvent(event: FlowEvent)
}
class GroupLeader(override val next: FlowHandler?) : FlowHandler {
override fun handleEvent(event: FlowEvent) {
when (event.fileID) {
// 根據(jù)文件ID分配審批權(quán)限 處理
111 -> print("GroupLeader收到流程:${event.filePath}")
// 否則轉(zhuǎn)發(fā)
else -> when (next) {
// 轉(zhuǎn)發(fā)到下一級
is FlowHandler -> {
println("GroupLeader: 此流程被轉(zhuǎn)發(fā)了")
next.handleEvent(event)
}
else -> println("GroupLeader: 此流程不能被處理")
}
}
}
}
class DepartmentLeader(override val next: FlowHandler?) : FlowHandler {
override fun handleEvent(event: FlowEvent) {
when (event.fileID) {
// 根據(jù)文件ID分配審批權(quán)限 處理
112 -> println("DepartmentLeader收到流程:${event.filePath}")
// 否則轉(zhuǎn)發(fā)
else -> when (next) {
// 轉(zhuǎn)發(fā)到下一級
is FlowHandler -> {
println("DepartmentLeader: 此流程被轉(zhuǎn)發(fā)了")
next.handleEvent(event)
}
else -> println("DepartmentLeader: 此流程不能被處理")
}
}
}
}
class CTO(override val next: FlowHandler?) : FlowHandler {
override fun handleEvent(event: FlowEvent) {
when (event.fileID) {
// 根據(jù)文件ID分配審批權(quán)限
113 -> println("CTO收到流程 :${event.filePath}昵骤,正在處理")
else -> println("CTO: 此流程不能被處理")
}
}
}
輸出:
GroupLeader: 此流程被轉(zhuǎn)發(fā)了
DepartmentLeader: 此流程被轉(zhuǎn)發(fā)了
CTO收到流程文件:filepath
以上可以參見責(zé)任鏈的工作機(jī)理,整個鏈條的每個處理環(huán)節(jié)都有 對其輸入?yún)?shù)的校驗肯适,只有輸入?yún)?shù)在責(zé)任鏈環(huán)節(jié)的有效接收范圍之內(nèi)变秦,才能處理相關(guān)的邏輯
偏函數(shù)
偏函數(shù)是數(shù)學(xué)中的概念,指的是定義域X中可能存在某些值在值域Y中沒有相對應(yīng)的值框舔。
一個多元函數(shù)傳入了部分參數(shù)之后的得到的新的函數(shù)蹦玫。
參數(shù)的固定一般使用默認(rèn)參數(shù)+具名參數(shù)。如果需要固定的參數(shù)在中間刘绣,雖然說可以通過具名參數(shù)來解決樱溉,但是很尷尬,因為必須使用一大堆具名參數(shù)纬凤。g
fun main(args: Array<String>) {
val chain = GroupLeader orElse DepartmentLeader orElse CTO
chain(FlowEvent("xxxxx",113))
}
/**
* 責(zé)任鏈模式中各環(huán)節(jié)對于輸入的校驗已經(jīng)處理邏輯的問題饺窿。
*
* 協(xié)變- 泛型對象作為函數(shù)的參數(shù),逆變- 泛型作為內(nèi)部方法的返回移斩。
*
* @param check 校驗函數(shù)
* @param handle 處理函數(shù)
*/
class ChainFunction<in P1, out R>(
private val check: (P1) -> Boolean,
private val handle: (P1) -> R) : (P1) -> R {
// 進(jìn)行有效性檢驗 傳遞參數(shù)p1
override fun invoke(p1: P1): R {
if (check(p1)) {
// 校驗通過執(zhí)行handle函數(shù)
return handle(p1)
} else {
// 檢驗不通過拋出異常
throw IllegalArgumentException("事件: ($p1) 無法被處理 ")
}
}
fun isChecked(p1: P1) = check(p1)
}
// 利用中綴函數(shù)形成引用鏈
infix fun <P1, R> ChainFunction<P1, R>.orElse(function: ChainFunction<P1, R>): ChainFunction<P1, R> {
return ChainFunction({
this.isChecked(it) || function.isChecked(it)
}) {
when {
this.isChecked(it) -> this(it)
else -> function(it)
}
}
}
val GroupLeader = {
val event: (FlowEvent) -> Boolean = {
it.fileID == 111
}
val handler: (FlowEvent) -> Unit = {
print("GroupLeader已處理該流程:filePath ${it.filePath}")
}
// 傳遞
ChainFunction(event,handler)
}()
val DepartmentLeader = {
val event: (FlowEvent) -> Boolean = {
it.fileID == 112
}
val handler: (FlowEvent) -> Unit = {
print("DepartmentLeader已處理該流程:filePath ${it.filePath}")
}
// 傳遞
ChainFunction(event,handler)
}()
val CTO = {
val event: (FlowEvent) -> Boolean = {
it.fileID == 113
}
val handler: (FlowEvent) -> Unit = {
print("CTO已處理該流程:filePath ${it.filePath}")
}
// 傳遞
ChainFunction(event,handler)
}()
CTO已處理該流程:filePath xxxxx
如果責(zé)任鏈過長,或者鏈上的事件傳遞需要判斷處理時間太長的話會影響性能绢馍,尤其是遞歸循環(huán)向瓷。
參考:
funKTionale
《Kotlin核心編程》