2018-03-05 15:50:32 2497次浏览 3条回答 0 悬赏 100 金钱

问题:
vendor/yiisoft/yii2/base/ErrorHandler.php 113行 $this->renderException($exception);
调用的是子类实现还是自己的抽象方法。因为在 vendor/yiisoft/yii2/base/ErrorHandler.php这个文件的 279行
定义了 abstract protected function renderException($exception);

而base/ErrorHandler的子类 vendor/yiisoft/yii2/web/ErrorHandler.php 实现了抽象方法 renderException
那么在调用这个子类已经实现的抽象方法,我理解的是在new子类对象时调用,怎么在上面父类里,如上所示描述:
vendor/yiisoft/yii2/base/ErrorHandler.php 113行 $this->renderException($exception);这里怎么调用了,
父类调用了自己的抽象方法的实现???

补充于 2018-03-05 15:53
写法1:
abstract class Parent{

    abstract public function dosomething();

    public function say(){
        $this->dosomething();
        echo "111111";
    }
}

class children extends Parent{
    public function dosomething(){
        echo "222222";
    }
}

调用:
    $c = new children()
    $c.say();

结果:
    222222
    111111


为什么不这样写:
写法2
abstract class Parent{

    abstract public function dosomething();

}

class children extends Parent{
    public function dosomething(){
        echo "222222";
    }

    public function say(){
        $this->dosomething();
        echo "111111";
    }
}

调用:
    $c = new children()
    $c.say();

结果:
    222222
    111111
    
写法1有什么好处?

补充于 2018-03-05 16:06

我的理解是:

在调用父类的方法时,实现了父亲的某些事情,同时完成了子类的实现(完成子类的实现,是在父类中完成的)。
补充于 2018-03-06 18:06

在看行为的代码时也发现了类似的写法:

    public function ensureBehaviors()
    {
        if ($this->_behaviors === null) {
            $this->_behaviors = [];
            foreach ($this->behaviors() as $name => $behavior) {   //$this->behaviors()这里是调用子类返回的数组,而子类又是重写父类的方法
                $this->attachBehaviorInternal($name, $behavior);
            }
        }
    }
补充于 2018-03-06 18:08

public function ensureBehaviors()
{
    // 为null表示尚未绑定
    // 多说一句,为空数组表示没有绑定任何行为
    if ($this->_behaviors === null) {
        $this->_behaviors = [];

        // 遍历 $this->behaviors() 返回的数组,并绑定
        foreach ($this->behaviors() as $name => $behavior) {
            $this->attachBehaviorInternal($name, $behavior);
        }
    }
}

这个方法主要是对子类用的, yii\base\Compoent 没有任何预先注入的行为,所以,这个调用没有用。 但是对于子类,你可能重载了 yii\base\Compoent::behaviros() 来预先注入一些行为。 那么,这个函数会将这些行为先注入进来。

最佳答案

  • Jeen 发布于 2018-03-05 17:51 举报

    首先解答一个疑问,不是“父类调用了子类的方法”,而是“子类继承了父类的方法,供这个子类的实例对象调用”。

    至于你提到的两种写法,怎么说呢, 原则上讲类的封装就是为了方便复用,所以写法一和写法二,本身都是对的。

    只是假如 Parent 类有另外一个子类(SubClass)也需要say方法,且say方法的逻辑流程与你给的流程完全一致时,那SubClass是否也需要实现一次say方法?

    通常答案是“no”,say方法从Parent类继承,我们只需要 在SubClass中实现它特定的dosomething 即可与 children 类进行区分。
    如果有特定情况,我们也可以在 SubClass 中重写 say 方法,实现其他特殊需求。

    额,说的比较模糊。欣赏你观察和思考问题的方式,但至于实际代码怎么写,见仁见智吧

    2 条回复
    回复于 2018-03-06 18:06 回复

    看我的补充

    回复于 2018-03-09 18:46 回复

    关于父类子类,同我之前说的,抽象方法可在子类中“实现”,具体方法可在子类中“重写”,当然不管是实现还是重写,实例化这个子类后,调用这个方法,执行的都是子类中的代码逻辑。另外,后面补充的,行为注入是框架提供的另外一种复用类方法的途径,跟父子类继承没有什么关系。 说的有点儿绕,不太明白你目前的疑问具体是什么。

    建议回到基础的关系上来看问题, 父类 子类 实例对象。通常类只提供属性和方法的定义以及继承,实际的调用是“实例对象”进行操作的,所以调用的具体流程还得看这个对象 实例化的是哪个类。

    其实我个人是不太建议在这些原理定义或者专业术语上花费太多时间的,大部分实际业务的实现,只需要知道相关的用法即可。
    当然摸清楚底层原理,对提升整体的理解力大有帮助,学其他东西也跟容易触类旁通。

    觉得很赞
  • 回答于 2018-03-08 10:36 举报

    首先对于结果来说,方法1和方法2都是正确的。
    但是,我们把一些公用的东西放到父类里面,而不是每个子类都重复来写,这样更符合封装的意义,也更灵活,而方法1正是遵循这样的原则

  • 回答于 2018-03-15 15:34 举报
    
    
您需要登录后才可以回答。登录 | 立即注册
灰太狼
副总裁

灰太狼

注册时间:2016-11-19
最后登录:2023-07-14
在线时长:76小时17分
  • 粉丝28
  • 金钱3290
  • 威望150
  • 积分5550

热门问题