65.9K
CodeProject 正在变化。 阅读更多。
Home

责任链模式

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.60/5 (7投票s)

2013年5月20日

CPOL

5分钟阅读

viewsIcon

34271

downloadIcon

169

使用实际示例轻松解释责任链模式。

引言

这是我第一篇CodeProject文章,我将解释一个重要的设计模式,称为职责链模式。

背景 

在解释职责链模式之前,我想先谈谈设计模式。设计模式对于开发大型软件解决方案非常有益,因为您可能知道大型软件解决方案需要某种类型的代码,这些代码具有更容易的代码修改、代码扩展的方式,并且这些代码行可以被其他编码人员使用,这就是所谓的代码重用。如果您是初学者,并且这是您尝试学习的第一个模式,我建议您收集这本书——《设计模式:可复用面向对象软件的基础》,并查看设计模式的维基页面。

为什么是职责链:

我不会从职责链模式的正式定义开始。而是考虑一个名为请求对象的对象,该对象必须由另一个或多个对象处理。更具体地说,我们可以说一组称为“负责对象”的对象将处理请求对象。然而,一个负责对象可能完全处理请求对象,或者多个负责对象可能部分处理请求。将有一系列负责对象,一个接一个,这就是该模式名为职责链的原因。请看下图:

上图演示了一个简单的负责对象链。首先,请求被发送到ResponsibleObject1。无论请求是否由ResponsibleObject1处理,请求都将由ResponsibleObject1本身发送到ResponsibleObject2。这样,一个请求可能被多个对象处理。如果您愿意,如果请求已被完全处理,您也可以阻止请求从一个负责对象传播到另一个。

好吧,您可能会认为总会有一个简单的直线负责对象链。但实际上并非如此。可能存在一个负责对象树,并且请求的传播将遵循树结构。例如,请看下图:

现在是时候揭示职责链的实际意图了。也许您已经理解了该模式的意图。无论您理解了什么,首先,让我陈述一下《设计模式》一书中是怎么说的。

避免将请求的发送者与其接收者耦合,让多个对象都有机会处理该请求。将接收对象串联起来,沿着链传递请求,直到某个对象处理它。

使用职责链模式,我们避免了“一个请求只会被一个接收者(负责对象)处理”的假设。这在实践中是不正确的,并且是该模式的一个非常重要的方面,让多个对象有机会处理请求非常有益,不是吗?

示例

我可能已经花了太多时间了。为了使本文简短,我现在将讲述我自己的一个实际例子……

考虑一个快递服务公司有几个部门来妥善处理客户请求。例如,一个部门可能专门处理重件包裹的客户请求。另一个部门可能负责处理大额款项的客户请求。可能有一个通用部门,可以处理包裹和金钱,但金额或包裹重量应该非常小。现在,让我们将这家快递服务公司与职责链模式进行映射。这里的客户订单是请求对象,每个部门是一个负责对象。然而,客户的请求将由哪个部门处理取决于客户包裹的重量或金额。如果客户想寄送包裹和金钱,那么客户的订单(请求)应该同时由包裹部门和金钱部门处理。

为了便于理解,让我们构建一个负责部门的树。树的根部应该是GeneralDivision。该部门最多可处理10公斤的重量和10000美元。ParcelDivisionOne可处理最多100公斤的包裹。ParcelDivisionTwo可处理无限重量(想象的)的包裹,而MoneyDivision可处理任何金额的款项。所有这些都是假设。无论我们假设了什么,让我们看看部门树:

我们将研究代码片段,但在此之前,这里是职责链模式的UML类图。根据UML类图,该模式中有三个参与者,它们是RequestRequestHandler,当然还有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来定义GeneralDivisionParcelDivisionOneParcelDivisionTwoMoneyDivision了。

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()函数来查看会发生什么。

© . All rights reserved.