责任链模式






4.60/5 (7投票s)
使用实际示例轻松解释责任链模式。
引言
这是我第一篇CodeProject文章,我将解释一个重要的设计模式,称为职责链模式。
背景
在解释职责链模式之前,我想先谈谈设计模式。设计模式对于开发大型软件解决方案非常有益,因为您可能知道大型软件解决方案需要某种类型的代码,这些代码具有更容易的代码修改、代码扩展的方式,并且这些代码行可以被其他编码人员使用,这就是所谓的代码重用。如果您是初学者,并且这是您尝试学习的第一个模式,我建议您收集这本书——《设计模式:可复用面向对象软件的基础》,并查看设计模式的维基页面。
为什么是职责链:
我不会从职责链模式的正式定义开始。而是考虑一个名为请求对象的对象,该对象必须由另一个或多个对象处理。更具体地说,我们可以说一组称为“负责对象”的对象将处理请求对象。然而,一个负责对象可能完全处理请求对象,或者多个负责对象可能部分处理请求。将有一系列负责对象,一个接一个,这就是该模式名为职责链的原因。请看下图:
上图演示了一个简单的负责对象链。首先,请求被发送到ResponsibleObject1。无论请求是否由ResponsibleObject1处理,请求都将由ResponsibleObject1本身发送到ResponsibleObject2。这样,一个请求可能被多个对象处理。如果您愿意,如果请求已被完全处理,您也可以阻止请求从一个负责对象传播到另一个。
好吧,您可能会认为总会有一个简单的直线负责对象链。但实际上并非如此。可能存在一个负责对象树,并且请求的传播将遵循树结构。例如,请看下图:
现在是时候揭示职责链的实际意图了。也许您已经理解了该模式的意图。无论您理解了什么,首先,让我陈述一下《设计模式》一书中是怎么说的。
避免将请求的发送者与其接收者耦合,让多个对象都有机会处理该请求。将接收对象串联起来,沿着链传递请求,直到某个对象处理它。
使用职责链模式,我们避免了“一个请求只会被一个接收者(负责对象)处理”的假设。这在实践中是不正确的,并且是该模式的一个非常重要的方面,让多个对象有机会处理请求非常有益,不是吗?
示例
我可能已经花了太多时间了。为了使本文简短,我现在将讲述我自己的一个实际例子……
考虑一个快递服务公司有几个部门来妥善处理客户请求。例如,一个部门可能专门处理重件包裹的客户请求。另一个部门可能负责处理大额款项的客户请求。可能有一个通用部门,可以处理包裹和金钱,但金额或包裹重量应该非常小。现在,让我们将这家快递服务公司与职责链模式进行映射。这里的客户订单是请求对象,每个部门是一个负责对象。然而,客户的请求将由哪个部门处理取决于客户包裹的重量或金额。如果客户想寄送包裹和金钱,那么客户的订单(请求)应该同时由包裹部门和金钱部门处理。
为了便于理解,让我们构建一个负责部门的树。树的根部应该是GeneralDivision。该部门最多可处理10公斤的重量和10000美元。ParcelDivisionOne可处理最多100公斤的包裹。ParcelDivisionTwo可处理无限重量(想象的)的包裹,而MoneyDivision可处理任何金额的款项。所有这些都是假设。无论我们假设了什么,让我们看看部门树:
我们将研究代码片段,但在此之前,这里是职责链模式的UML类图。根据UML类图,该模式中有三个参与者,它们是Request
、RequestHandler
,当然还有Client
。
代码
让我们研究代码片段。首先,我们需要生成客户订单(请求对象)。下面的Request
类将生成请求对象:
class Request { private double weight; private double money; public Request(double w, double m) { this.weight = w; this.money = m; } public double getWeight() { return this.weight; } public double getMoney() { return this.money; } }
现在我将创建一个名为Division
的抽象类。通过扩展这个抽象类,我们将创建负责的类,每个类代表快递服务公司的一个特定部门。让我们看看Division
抽象类有什么。
abstract class Divison { protected List nextList = new ArrayList(); public void addNextDivison(Divison d) { this.nextList.add(d); } public void handle(Request request) { for(int i=0; i < nextList.size(); i++) { Divison d = nextList.get(i); d.handle(request); } } protected abstract void processRequest(); }
nextList
是下一个负责对象的列表。这有意义吗?那么,如果一个部门(负责对象)无法处理客户订单(请求),它会怎么做?在这种情况下,它将通过handle(Request request)
函数将订单发送到存储在nextList
中的部门。
processRequest()
是一个抽象函数,将在具体类中定义,该函数将包含处理/处理请求的实际代码。
现在是时候通过扩展Division
来定义GeneralDivision
、ParcelDivisionOne
、ParcelDivisionTwo
、MoneyDivision
了。
class GeneralDivison extends Divison { @Override public void handle(Request request) { if(request.getWeight() <= 10 && request.getMoney() <= 10000) processRequest(); else super.handle(request); } public void processRequest() { System.out.println("This request is processed by -- General Divison"); } } class ParcelDivisonOne extends Divison { @Override public void handle(Request request) { if(request.getWeight() <= 100) processRequest(); else super.handle(request); } public void processRequest() { System.out.println("This request is processed by -- ParcelDivisonOne"); } } class ParcelDivisonTwo extends Divison { @Override public void handle(Request request) { if(request.getWeight() > 0) processRequest(); } public void processRequest() { System.out.println("This request is processed by -- ParcelDivisonTwo"); } } class MoneyDivison extends Divison { @Override public void handle(Request request) { if(request.getMoney() > 0) processRequest(); } public void processRequest() { System.out.println("This request is processed by -- MoneyDivison"); } }
我们曾经关联过负责的部门吗?显然,我们需要关联负责对象,无论是链式还是树状职责。下面的代码易于理解,它实现了负责对象之间的这种关系,并演示了客户端如何使用一个负责对象来处理请求。
public class CourierServiceDemo { private static Divison createDivison() { Divison div1 = new GeneralDivison(); Divison div2 = new ParcelDivisonOne(); Divison div3 = new ParcelDivisonTwo(); Divison div4 = new MoneyDivison(); div2.addNextDivison(div3); div1.addNextDivison(div2); div1.addNextDivison(div4); return div1; } public static void main(String[] args) { Divison div = createDivison(); div.handle(new Request(101, 10)); } }
现在轮到您更改请求对象数据或创建新请求对象,并将它们传递给handle()
函数来查看会发生什么。