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

时间:2015-10-04 18:48:53

标签: php oop abstraction

我对这个用例有一个普遍的问题:我有一个班级A。这个类有一个非抽象的方法doStuffCallback(),它可以被覆盖,但并不是每个子类都需要。但是:我想确保如果方法被覆盖,则子类方法必须调用parents方法。

示例:

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中是否有办法强制覆盖方法来调用parent方法?

4 个答案:

答案 0 :(得分:6)

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

相反,程序必须依赖明确的文档合同;并希望通过单元测试来确保一致性。

也可以使用防护,使得在某个时刻使用父类的方法时,如果“当前状态”无效,它可能会抛出异常(例如,这样的方法具有尚未被召唤)。通过使子类需要调用(如文档合同中所定义)一些特殊方法而不是简单地覆盖超级方法,这也可以更明确。但是,这种情况不属于任何类型的系统。

虽然# Resources for cities. resources :cities, only: :index do get :autocomplete, :on => :collection end 范围可以使用(例如,调用覆盖方法的调用非重写方法),但这将涉及进一步的魔法(例如,某些堆栈状态)以避免无限递归循环;并且很容易意外地忽略使用。

我的建议是调用一个(私有)方法,该方法调用这个'可能覆盖'方法与任何适用的逻辑相关,如示例所示(尽管希望有更多任务特定的驯服)。然后,不期望或不需要(受保护的)覆盖方法来处理任何特殊逻辑本身;它也不是直接在父类所建立的上下文之外调用的 - 它正是它目前声称的特殊回调。

答案 1 :(得分:3)

我倾向于不同意“这非常难看”。它是处理此用例的标准方法,也是Template Method Pattern的变体。

现在我只是在猜测,因为你没有提供一个真实的例子,但如果你说这两种方法“做同样的事情”,你的设计可能会出现问题。如果他们做同样的事情,如果子类以不同的方式做同样的事情,为什么要调用父实现呢?对我来说,听起来这个方法确实不止一件事,你可以将它分解为几个可以单独覆盖的部分(或者不是,然后将它们设为私有或最终)。 / p>

答案 2 :(得分:0)

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

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”,然后调用一个新的“Abstract”函数来强制childs实现它。如果您不想强制使用新的“抽象”功能,只需将其抽象化即可。

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

答案 3 :(得分:-2)

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

<?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