【问题标题】:Php: Overriding abstract method goes wrongPHP:覆盖抽象方法出错
【发布时间】:2018-12-27 14:43:45
【问题描述】:

我认为php的OOP实现有问题。

编辑:考虑更多说明性示例:

abstract class Animal {

    public $name;

    // public function Communicate(Animal $partner) {}     // Works
    public abstract function Communicate(Animal $partner); // Gives error
}

class Panda extends Animal {

    public function Communicate(Panda $partner) {
        echo "Hi {$partner->name} I'm a Panda";
    }

}

class Human extends Animal {

    public function Communicate(Human $partner) {
        echo "Hi {$partner->name} I'm a Human";
    }

}

$john = new Human(); $john->name = 'John';
$mary = new Human(); $mary->name = 'Mary';
$john->Communicate($mary); // should be ok

$zuzi = new Panda(); $zuzi->name = 'Zuzi';
$zuzi->Communicate($john); // should give error

问题是当Animal::Communicate是抽象方法时,php告诉下面的方法是非法的:

"公共函数 Communicate(Panda $partner)" "公共功能沟通(Human $partner)"

但是当 Animal::Communicate 是非抽象的但具有零实现的时候,Php 认为这些方法是合法的。所以在我看来这是不对的,因为我们在这两种情况下都进行了覆盖,而且这两种情况是相等的,所以这似乎是一个错误......

帖子的旧部分:

请考虑以下代码:

Framework.php

namespace A
{
    class Component { ... }

    abstract class Decorator {
        public abstract function Decorate(\A\Component $component);
    }
}

Implementation.php

namespace B
{
    class MyComponent extends \A\Component { ... }
}

MyDecorator.php

namespace A
{
    class MyDecorator extends Decorator {
        public function Decorate(\B\MyComponent $component) { ... }
    }
}

以下代码在 MyDecorator.php 中给出错误提示

致命错误:MyDecorator::Decorate() 的声明必须与在线MyDecorator.php 中的A\Decorator::Decorate() 的声明兼容...

但是当我将 Framework.php::Decorator 类更改为以下实现时:

    abstract class Decorator {
        public function Decorate(\A\Component $component) {}
    }

问题消失了。

【问题讨论】:

    标签: php


    【解决方案1】:

    我不确定(还没有测试过;),但是你声明了这个抽象函数:

    public abstract function Decorate(\A\Component $component);
    

    所以你应该像那样实现它。但你这样做了:

    public function Decorate(\B\MyComponent $component) { ... }
    

    那不一样。你能试着把它改成\A\Component吗?

    致所有 cmets:事实上,这段 PHP “运行”

    namespace A
    {
        class Component { }
    
        abstract class Decorator {
            public abstract function Decorate(\A\Component $component);
        }
    }
    namespace B
    {
        class MyComponent extends \A\Component { }
    }
    namespace A
    {
        class MyDecorator extends Decorator {
            public function Decorate(\A\Component $component) {}
        }
    
    }
    

    这不是:

    <?php
    namespace A
    {
        class Component { }
    
        abstract class Decorator {
            public abstract function Decorate(\A\Component $component);
        }
    }
    namespace B
    {
        class MyComponent extends \A\Component { }
    }
    namespace A
    {
        class MyDecorator extends Decorator {
            public function Decorate(\B\MyComponent $component) {}
        }
    
    }
    ?>
    

    出现此错误:PHP Fatal error: Declaration of A\MyDecorator::Decorate() must be compatible with that of A\Decorator::Decorate() in line 18

    现在您可以就应该或不应该如何进行讨论,但这是代码的问题。

    所以,为了满足我自己的好奇心:这也是非法的:

    <?php
        class Component { }
    
        abstract class Decorator {
            public abstract function Decorate(Component $component);
        }
    
        class MyComponent extends Component { }
        class MyDecorator extends Decorator {
            public function Decorate(MyComponent $component) {}
        }
    
    
    ?>
    

    这不是命名空间或任何东西。这似乎不合法。

    【讨论】:

    • 此模式将类型限制为更具体的类型,这应该不是问题,因为 \B\MyComponent 扩展了 \A\Component ,这意味着 \B\MyComponent 是第一名
    • 但是你的摘要让你定义一个接受所有\A\Component参数的函数,而你的实现只接受\B\MyComponent。这意味着您还没有实现超级“要求”的功能。
    • 不,Lu4 是对的,因为更具体的类必须是子类型并满足其超类存在的所有要求。它被称为 Liskow 的替换原则,是 OOP 的基本规则......
    • 可能是命名空间的问题,可能你的liskow的替换原则在PHP中没有遵循,但这仍然是它不起作用的原因。请参阅我的答案中的示例编辑。
    • 好吧,我没有看到任何问题了。你必须按照我在 PHP 中发布的(所以没有派生类)来做。这可能是一个错误,但这是我们同意的情况,对吧?真的不知道要添加什么了....
    【解决方案2】:

    请参阅http://bugs.php.net/bug.php?id=36601,此问题已被报告为错误但因懒惰而被拒绝:D

    【讨论】:

    • 问题是类型提示通常允许专门化,但当方法是抽象的时不允许它,所以我怀疑他们实际上自 2006 年以来已经实现了这个功能,但是忘记了一些东西,当方法时它会出错是抽象的
    【解决方案3】:

    它与抽象无关。它与类型提示有关。该方法的两个定义不兼容,因为您将参数显式设置为\A\Component 类型,然后尝试使用\B\Component 重载该方法,您不能这样做,因为它会更改方法签名。 Decorate 的任何后续声明必须使用与其父声明相同的类型提示,以使方法签名兼容。

    【讨论】:

    • 是的,但是,\B\Component 扩展了 \A\Component,这意味着签名在 OOP 方面是相等的,当方法具有零实现时,这个东西正在工作,并且当我将其标记为抽象并删除零实现,它表明方法不兼容。我不认为方法的实现是方法签名兼容性的主题
    • 实际上这是他们在 PHP 中搞砸的一个主题(就像许多其他人一样)......严格来说,在 OO 语言中必须可以使用子类型进行类型提示(实际上 PHP 只是因为合理的时间)...
    【解决方案4】:

    这可能会对某人有所帮助,而且还不晚。 处理这种情况的最佳方法是使用接口。 下面考虑;

    <?php
        interface Componentor{}
    
        class Component implements Componentor { }
    
        abstract class Decorator {
            public abstract function Decorate(Componentor $component);
        }
    
        class MyComponent extends Component { }
    
        class MyDecorator extends Decorator {
            public function Decorate(Componentor $component) {}
        }
    
    ?>
    

    用法;

    <?php
        $c=new Component();
        //TODO ....blah blah blah...
        $decor=new MyDecorator();
        //TODO ....blah blah blah...
        $decor->Decorate($c);
    
    ?>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-09-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-31
      • 1970-01-01
      相关资源
      最近更新 更多