餐馆的故事-浅析职责链模式

简介:

我们在餐馆吃饭的时候,一般都是在拿到菜单后,选择喜欢的菜,然后通知服务员。服务员会将我们的定单交给大厨,大厨可能会亲自去做这道菜,也可能安排给小厨来做,总之,我们不用担心他们没有人做菜,即使有时候等的时间长点。

下面我们来分析一下。首先,对于我们这些点菜的人来说,我们一般不了解这些厨师,我们没法找到某个具体的厨师让他去做,所以只好把请求交给服务员;然后,对于餐馆的服务员、大厨、小厨来说,他们都可以接受并处理这个请求,但很明显,他们有分工,不会一人去做所有的菜。

简单地说,顾客发送请求(点菜),餐馆的人接受请求(拿到定单),有多个人可以处理该请求(做菜),或者说履行职责,但最后只有一人处理该请求。如下图所示:

CoR

这当中就包含了职责链模式(Chain Of Responsibility,简称CoR)。

我们来看看Gof中CoR的描述:

意图

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。

从第一个对象开始,链中收到请求的对象要么亲自处理它,要么转发给链中的下一个候选者,请求发送者不确定到底哪个对象会处理它,——我们称该请求有一个隐式的接受者(implicit receiver)。

在餐馆的例子中,顾客是请求的发送者,接受者则是服务员、大厨、小厨,他们构成了一条职责链,他们当中会一个人来处理请求。

好了,菜终于上来了,先把它吃掉吧...

现在走出餐馆,我们来看看在一般场景下,CoR的类图是:

CoR2

参与者

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,如需转载请自行联系原作者。

目录
相关文章
|
1天前
|
数据采集 人工智能 安全
|
10天前
|
云安全 监控 安全
|
2天前
|
自然语言处理 API
万相 Wan2.6 全新升级发布!人人都能当导演的时代来了
通义万相2.6全新升级,支持文生图、图生视频、文生视频,打造电影级创作体验。智能分镜、角色扮演、音画同步,让创意一键成片,大众也能轻松制作高质量短视频。
910 150
|
15天前
|
机器学习/深度学习 人工智能 自然语言处理
Z-Image:冲击体验上限的下一代图像生成模型
通义实验室推出全新文生图模型Z-Image,以6B参数实现“快、稳、轻、准”突破。Turbo版本仅需8步亚秒级生成,支持16GB显存设备,中英双语理解与文字渲染尤为出色,真实感和美学表现媲美国际顶尖模型,被誉为“最值得关注的开源生图模型之一”。
1646 8
|
6天前
|
人工智能 前端开发 文件存储
星哥带你玩飞牛NAS-12:开源笔记的进化之路,效率玩家的新选择
星哥带你玩转飞牛NAS,部署开源笔记TriliumNext!支持树状知识库、多端同步、AI摘要与代码高亮,数据自主可控,打造个人“第二大脑”。高效玩家的新选择,轻松搭建专属知识管理体系。
365 152
|
7天前
|
人工智能 自然语言处理 API
一句话生成拓扑图!AI+Draw.io 封神开源组合,工具让你的效率爆炸
一句话生成拓扑图!next-ai-draw-io 结合 AI 与 Draw.io,通过自然语言秒出架构图,支持私有部署、免费大模型接口,彻底解放生产力,绘图效率直接爆炸。
604 152
|
9天前
|
人工智能 安全 前端开发
AgentScope Java v1.0 发布,让 Java 开发者轻松构建企业级 Agentic 应用
AgentScope 重磅发布 Java 版本,拥抱企业开发主流技术栈。
570 13
|
2天前
|
编解码 人工智能 机器人
通义万相2.6,模型使用指南
智能分镜 | 多镜头叙事 | 支持15秒视频生成 | 高品质声音生成 | 多人稳定对话