我們在餐館吃飯的時候,一般都是在拿到菜單后,選擇喜歡的菜,然后通知服務員。服務員會將我們的定單交給大廚,大廚可能會親自去做這道菜,也可能安排給小廚來做,總之,我們不用擔心他們沒有人做菜,即使有時候等的時間長點。
下面我們來分析一下。首先,對于我們這些點菜的人來說,我們一般不了解這些廚師,我們沒法找到某個具體的廚師讓他去做,所以只好把請求交給服務員;然后,對于餐館的服務員、大廚、小廚來說,他們都可以接受并處理這個請求,但很明顯,他們有分工,不會一人去做所有的菜。
簡單地說,顧客發送請求(點菜),餐館的人接受請求(拿到定單),有多個人可以處理該請求(做菜),或者說履行職責,但最后只有一人處理該請求。如下圖所示:
這當中就包含了職責鏈模式(Chain Of Responsibility,簡稱CoR)。
我們來看看Gof中CoR的描述:
意圖
使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關系。將這些對象連成一條鏈,并沿著這條鏈傳遞請求,直到有一個對象處理它為止。
從第一個對象開始,鏈中收到請求的對象要么親自處理它,要么轉發給鏈中的下一個候選者,請求發送者不確定到底哪個對象會處理它,——我們稱該請求有一個隱式的接受者(implicit receiver)。
在餐館的例子中,顧客是請求的發送者,接受者則是服務員、大廚、小廚,他們構成了一條職責鏈,他們當中會一個人來處理請求。
好了,菜終于上來了,先把它吃掉吧...
現在走出餐館,我們來看看在一般場景下,CoR的類圖是:
參與者
Handler(如Employee)
——定義一個處理請求的接口;
ConcreteHandler(如Server和Chef)
——處理它負責的請求;
——可訪問它的后繼者(Successor);
——如果可處理該請求,處理之;否則轉發給后繼者;
Client
——向職責鏈提交請求。
適用性
1、有多個的對象可以處理一個請求,而具體的處理者在運行時自動確定;
2、希望在對接受者不了解的情況下,向多個對象的一個提交請求;
3、處理請求的對象集合需要動態指定。
示例代碼:
using System;
?
namespace ChainOfPatterns
{
class Program
{
static void Main(string[] args)
{
// 餐館工作人員
Server server = new Server("anders");
Chef chef = new Chef("dudu");
AssistantChef ac = new AssistantChef("bill");
server.SetSuccessor(chef);
chef.SetSuccessor(ac);
?
Customer customer = new Customer();
// 點第一道菜
customer.OrderName = "酸辣土豆絲";
server.HandleRequest(customer);
?
// 點第二道菜
customer.OrderName = "農家小炒肉";
server.HandleRequest(customer);
?
Console.ReadLine();
}
}
?
public class Customer
{
private string orderName;
?
public string OrderName
{
get { return orderName; }
set { orderName = value; }
}
}
?
public abstract class Employee
{
protected string name;
protected Employee successor;
?
public Employee(string name)
{
this.name = name;
}
?
public void SetSuccessor(Employee successor)
{
this.successor = successor;
}
?
public virtual void HandleRequest(Customer customer)
{
if (successor != null)
{
successor.HandleRequest(customer);
}
}
}
?
/// <summary>
/// 服務員不用炒菜,所以直接轉發給后繼者。
/// </summary>
public class Server : Employee
{
public Server(string name) : base(name)
{
}
}
?
public class Chef : Employee
{
public Chef(string name) : base(name)
{
}
?
public override void HandleRequest(Customer customer)
{
if (customer.OrderName == "農家小炒肉")
{
Console.WriteLine("{0}做的{1}", name, customer.OrderName);
}
else
{
base.HandleRequest(customer);
}
}
}
?
public class AssistantChef : Employee
{
public AssistantChef(string name) : base(name)
{
}
?
public override void HandleRequest(Customer customer)
{
if (customer.OrderName == "酸辣土豆絲")
{
Console.WriteLine("{0}做的{1}", name, customer.OrderName);
}
else
{
base.HandleRequest(customer);
}
}
}
}
注意:
如果我們運氣很差,點了一個沒有存貨的菜,那么不管是服務員還是大廚、小廚都沒法處理了。也就是說,對于一個請求,可能沒有任何接受者會處理它。
問題:
我還想到一個問題,如果一道菜很復雜,需要大廚和小廚一起做,該怎么辦呢?望各路高手指點迷津 :)
其它的例子:
瀏覽器事件模型
假設我們的HTML頁面上有一個<div />,它又包含了一個<input />按鈕,對于按鈕的click事件來說,在IE中它的觸發順序為從最特定的事件目標(button)道最不特定的事件目標(document對象)。input,div,body到document,它們也構成了一條職責鏈。
擊鼓傳花
擊鼓傳花是一種熱鬧而又緊張的飲酒游戲。在酒宴上賓客依次坐定位置,由一人擊鼓,擊鼓的地方與傳花的地方是分開的,以示公正。開始擊鼓時,花束就開始依次傳遞,鼓聲一落,如果花束在某人手中,則該人就得飲酒。
看起來,酒宴上的賓客構成了一條鏈。但我認為這是CoR的一個反面例子。對于每一輪具體的游戲來說,這些賓客唯一能做的事情就是將花束傳遞(轉發給后繼者),沒有誰能夠主動停下來,而能夠處理請求的人(擊鼓者)到底是誰的后繼者呢?這個并不確定,職責鏈如何構建呢?
另外,建議看一看下面兩篇文章中高手的論述:職責鏈模式在開發中的應用,手拉手就是職責鏈嗎?
?
參考:
《設計模式-可復用面向對象軟件的基礎》 Gof
《UML基礎、案例與應用》Joseph Schmuller
http://www.dofactory.com/Patterns/PatternChain.aspx
本文轉自一個程序員的自省博客園博客,原文鏈接:http://www.cnblogs.com/anderslly/archive/2008/02/28/chainOfResponsibility.html,如需轉載請自行聯系原作者。