责任链

假设我们有一组对象,它们要共同解决一个问题。当一个对象无法解决问题时,我们希望该对象将任务发送给指定链中的另一个对象。这就是责任链设计模式的用途。

为了实现这一目标,我们需要一个处理程序,也就是我们的 Chain 接口。链中的各个对象都将实现这个 Chain 接口。

让我们从一个简单的例子开始:助理可以购买低于 100 美元的资产,经理可以购买低于 500 美元的东西。

我们对 Purchaser 接口的抽象是这样的:

Unresolved include directive in modules/ROOT/pages/ch05/ch5-06.adoc - include::example$Chapter 5/ChainOfResponsibility/Purchaser.php[]

我们首先要实现的是 Associate 类。很简单,我们实现 setNextPurchaser 函数,这样它就会将 nextPurchaser 类属性设置为链中的下一个对象。

当我们调用 buy 函数时,如果价格在范围之内,关联者就会购买。如果不在范围内,则由链中的下一个购买者购买:

Unresolved include directive in modules/ROOT/pages/ch05/ch5-06.adoc - include::example$Chapter 5/ChainOfResponsibility/AssociatePurchaser.php[]

我们的经理类完全相同;我们只是允许经理购买低于 500 美元的资产。实际上,当你应用这种模式时,你不会只是复制一个类,因为你的类会有不同的逻辑;这个例子只是一个非常简单的实现。

下面是代码:

Unresolved include directive in modules/ROOT/pages/ch05/ch5-06.adoc - include::example$/Chapter 5/ChainOfResponsibility/ManagerPurchaser.php[]

让我们在 index.php 文件中从 associate 处进行基本购买。

首先,这是我们放入 index.php 文件中的代码:

<?php

require_once('Purchaser.php');
require_once('AssociatePurchaser.php');

$associate = new AssociatePurchaser();

$associate->buy(50);

所有这些的输出如下:

image 2023 10 31 10 03 29 527

接下来,让我们测试我们的 Manager 类。我们将在 index.php 文件中修改购买价格,并将 Manager 类添加到链中。

这是我们修改后的 index.php:

<?php

require_once('Purchaser.php');
require_once('AssociatePurchaser.php');
require_once('ManagerPurchaser.php');

$associate = new AssociatePurchaser();
$manager = new ManagerPurchaser();

$associate->setNextPurchaser($manager);

$associate->buy(400);

这有以下输出:

image 2023 10 31 10 05 24 363

让我们看看如果我们改变价格导致购买失败会发生什么。

我们更改了 index.php 文件的最后一行,因此购买价格现在为 600 美元:

<?php

require_once('Purchaser.php');
require_once('AssociatePurchaser.php');
require_once('ManagerPurchaser.php');

$associate = new AssociatePurchaser();
$manager = new ManagerPurchaser();

$associate->setNextPurchaser($manager);

$associate->buy(600);

这有以下输出:

image 2023 10 31 10 06 37 454

我们现在可以扩展这个脚本。让我们添加 DirectorPurchaserBoardPurchaser,以便我们可以以更高的成本进行采购。

我们将创建一个可以购买 10,000 美元以下商品的 DirectorPurchaser

这个类如下:

Unresolved include directive in modules/ROOT/pages/ch05/ch5-06.adoc - include::example$/Chapter 5/ChainOfResponsibility/DirectorPurchaser.php[]

让我们对可以购买低于 100,000 美元的 BoardPurchaser 类执行相同的操作:

Unresolved include directive in modules/ROOT/pages/ch05/ch5-06.adoc - include::example$Chapter 5/ChainOfResponsibility/BoardPurchaser.php[]

现在我们可以更新我们的 index.php 脚本以需要新的类,实例化它们,然后将所有内容绑定在一个链中。最后,我们将尝试通过调用链中的第一个来运行购买。

下面是脚本:

<?php

require_once('Purchaser.php');
require_once('AssociatePurchaser.php');
require_once('ManagerPurchaser.php');
require_once('DirectorPurchaser.php');
require_once('BoardPurchaser.php');

$associate = new AssociatePurchaser();
$manager = new ManagerPurchaser();
$director = new DirectorPurchaser();
$board = new BoardPurchaser();

$associate->setNextPurchaser($manager);
$manager->setNextPurchaser($director);
$director->setNextPurchaser($board);

$associate->buy(11000);

下面是该脚本的输出:

image 2023 10 31 10 10 26 276

这样,我们就可以遍历对象链来处理数据。这在处理树形数据结构(例如 XML 树)时尤其有用。这可以以启动和离开的方式进行,我们可以降低处理链中迭代的开销。

此外,链是松散耦合的,数据通过链传递,直至处理完毕。任何对象都可以以任何顺序链接到其他任何对象。