PHP中的方法链接

示例

方法链接是Martin Fowler的《领域特定语言》一书中介绍的一种技术。方法链接总结为

使修饰符方法返回宿主对象,以便可以在单个表达式中调用多个修饰符

看这段非链接/常规代码(从上述书籍中移植到PHP)

$hardDrive = new HardDrive;
$hardDrive->setCapacity(150);
$hardDrive->external();
$hardDrive->setSpeed(7200);

通过方法链接,您可以以更紧凑的方式编写上述语句:

$hardDrive = (new HardDrive)
    ->setCapacity(150)
    ->external()
    ->setSpeed(7200);

为此,您所需要做的就是使用要链接return $this的方法:

class HardDrive {
    protected $isExternal = false;
    protected $capacity = 0;
    protected $speed = 0;

    public function external($isExternal = true) {
        $this->isExternal = $isExternal;        
        return $this; // 返回当前的类实例以允许方法链接
    }

    public function setCapacity($capacity) {
        $this->capacity = $capacity;        
        return $this; // 返回当前的类实例以允许方法链接
    }

    public function setSpeed($speed) {
        $this->speed = $speed;        
        return $this; // 返回当前的类实例以允许方法链接
    }
}


什么时候使用

使用方法链接的主要用例是在构建内部领域特定语言时。方法链接是表达式构建器和Fluent接口中的构建块。但是,它不是这些的同义词。方法链接仅启用这些功能。引用福勒:

我还注意到一个常见的误解-许多人似乎将流畅的界面与“方法链”等同。当然,链接是与流利的接口一起使用的常用技术,但是真正的流利性远不止于此。

话虽如此,使用方法链接只是为了避免编写宿主对象,这被许多人视为代码异味。它适用于不明显的API,尤其是在与非链接API混合使用时。


补充说明

命令查询分离

命令查询分离是Bertrand Meyer提出的设计原则。它指出,使状态发生变化的方法(命令)不应返回任何内容,而返回某些状态的方法(查询)不应使状态发生变化。这使得对系统进行推理变得更容易。方法链接违反了这一原理,因为我们正在改变状态返回某些东西。

吸气剂

在使用实现方法链接的类时,在调用getter方法(即返回除之外的方法的方法)时要特别注意$this。由于getter必须返回除以外的值$this,因此将其他方法链接到getter上会使调用对获得的值进行操作,而不是对原始对象进行操作。尽管有一些链式吸气剂的用例,但它们可能会使代码的可读性降低。

德米特定律及其对测试的影响

上面介绍的方法链接不违反Demeter法则。它也不会影响测试。那是因为我们返回的是主机实例,而不是协作者。这是一个普遍的误解,源于人们将纯粹的方法链接与Fluent接口Expression Builders混淆了。仅当方法链接返回除宿主对象以外的其他对象时,您才违反Demeter定律并最终在测试中出现模拟节。