OOP (PHP) - 强制重写方法根据父方法调用

OOP (PHP) - Force overridden method to call according parent-method

我对这个用例有一个普遍的问题:我有一个 class A。这个 class 有一个非抽象方法 doStuffCallback() 可以被重写,但它不是每个子 class 都必需的。但是:我想确保如果该方法被覆盖,subclass-方法必须调用父方法。

示例:

abstract class A {
    private function doStuff() {
        $this->doStuffCallback();
    }

    protected function doStuffCallback() {
        // IMPORTANT CODE HERE
    }
}

class B extends A {
    protected function doStuffCallback() {
        parent::doStuffCallback(); // I want to enforce this because the parents method code is important

        // ALSO IMPORTANT CODE
    }
}

因为被覆盖的方法做同样的事情,所以为相同的责任定义两个方法和一个调用这两个方法的私有帮助器方法将是非常丑陋的。像这样:

abstract class A {
    private function doStuff() {
        $this->callDoStuffCallback();
    }

    private function callDoStuffCallback() {
        $this->internalDoStuffCallback();
        $this->doStuffCallback();

        // This is VERY ugly
    }

    private function internalDoStuffCallback() {
        // IMPORTANT CODE HERE
    }

    protected function doStuffCallback() {}
}

class B extends A {
    protected function doStuffCallback() {
        // IMPORTANT CODE
    }
}

这真是又丑又费劲。所以我的问题是:在 PHP 中有没有办法强制覆盖方法调用父方法?

不,你可以访问,你可以使用父方法,像这样

<?php

class A {

   function s1($p1) {
      echo 's1: '.$p1;
   }
}


class B extends A {

    public function callParent($method, $p1) {
        parent::$method($p1);
    }
}


$b = new B();

$b->callParent('s1', 'param1');

或替换扩展魔法方法 __call 等 https://github.com/StagnantIce/php_extend_magic/blob/master/AExtendClass.php

没有。 PHP中没有这样的语言功能;在大多数子类型 'OO' 语言中,此限制是不可能的。

相反,程序必须依赖于明确的文档契约;希望进行单元测试以确保一致性。


也可以使用守卫,这样,在某些时候,当使用父 class 上的方法时,如果 'current state' 无效(例如. 尚未调用某某方法)。这也可以通过使 subclass 需要调用(如文档契约中定义的)一些特殊方法而不是简单地覆盖 super 方法来变得更加明确。但是,这不属于任何类型系统。

虽然可以使用 self:: 范围 (例如调用非覆盖方法调用覆盖方法),这将涉及更多魔法(例如一些堆栈状态)以避免无限递归循环;而且很容易不小心遗漏用法。

我的建议是调用一个(私有)方法,该方法根据应用的任何逻辑调用此 'maybe overriden' 方法,如示例中所示(尽管希望有更多特定于任务的驯服)。然后(受保护的)覆盖方法不需要或不需要处理任何特殊逻辑本身;它也不意味着在父 class 建立的上下文之外直接调用 - 这正是它目前声称的,一个特殊的回调。

我倾向于不同意 "This is VERY ugly"。这是处理此用例的标准方法,也是 Template Method Pattern.

的变体

现在我只是猜测,因为你没有提供一个真实的例子,但是如果你说这两种方法"do the same thing",你的设计可能有问题。如果他们做同样的事情,如果子类以不同的方式做同样的事情,为什么还要调用父实现呢?对我来说,听起来该方法实际上 不止一件事 并且您可以将其分解为几个可以单独覆盖的部分(或者不覆盖,然后将它们设为私有或最终).

我知道这是一个老话题,但我也在问自己同样的问题,我所做的是:

abstract class A {
    private function doStuff() {
        $this->doStuffCallback();
    }

    final protected function doStuffCallback() {
        // IMPORTANT CODE HERE

        $this->callNewFunction();
    }

    abstract protected function callNewFunction();
} 

class B extends A {
    protected function callNewFunction() {
        // ALSO IMPORTANT CODE
    }
}

所以基本上我会标记为 "final" 您希望为每个 child 强制代码的函数,然后调用一个新的 "Abstract" 函数来强制 child实施它。如果您不想强制使用新的 "Abstract" 函数,请不要将其抽象化。

编辑:这基本上是@Fabian Schmengler 的回答,但您的示例更具体。