PHP中的「重載」是個啥?

不少面試官在面試的時候都會問一些面向對象的問題,面向對象的三大特性中,多態最主要的實現方式就是方法的重載和重寫。可是在PHP中,只有重寫,並無徹底的重載能力的實現。php

重寫,子類重寫父類方法。git

// 重寫
class A
{
    public function test($a)
    {
        echo 'This is A:' . $a, PHP_EOL;
    }
}

class childA extends A
{
    public function test($a)
    {
        echo 'This is A child:' . $a, PHP_EOL;
    }
}

$ca = new childA();
$ca->test(1);

這個在PHP中是沒有任何問題的,子類能夠重寫父類的方法。當實例化子類的時候,調用的就是子類實現的重寫的方法。github

重載,相同方法名但參數數量或者類型不一樣。面試

class A{
    function foo($a){
        echo $a;
    }
    // Fatal error: Cannot redeclare A::foo()
    function foo($a, $b){
        echo $a+$b;
    }
}

抱歉,這樣寫的結果將會是直接的報錯。PHP並不支持這樣的重載能力。而在PHP的官方手冊上,重載的定義是使用__set()、__get()、__call()、__callStatic()等魔術方法來對沒法訪問的變量或方法進行重載。這與咱們所學習的面向對象中的重載徹底不一樣,在手冊中的note裏也有不少人對此提出了疑問。固然,咱們今天並不會再去講這些魔術方法的使用。關於它們的使用能夠參考咱們以前寫過的文章:PHP中的那些魔術方法(一)PHP的那些魔術方法(二)算法

那麼,在PHP中能夠實現重載嗎?固然能夠,只不過會麻煩一些:微信

// 重載
class B
{
    public function foo(...$args)
    {
        if (count($args) == 2) {
            $this->fooAdd(...$args);
        } else if (count($args) == 1) {
            echo $args[0], PHP_EOL;
        } else {
            echo 'other';
        }
    }

    private function fooAdd($a, $b)
    {
        echo $a + $b, PHP_EOL;
    }
}

$b = new B();
$b->foo(1);
$b->foo(1, 2);

使用一個方法來調用其餘方法,根據參數數量來進行判斷,就能夠實現參數數量不一樣的方法重載。oop

// 使用__call()進行重載
class C
{
    public function __call($name, $args)
    {
        if ($name == 'foo') {
            $funcIndex = count($args);
            if (method_exists($this, 'foo' . $funcIndex)) {
                return $this->{'foo' . $funcIndex}(...$args);
            }
        }
    }

    private function foo1($a)
    {
        echo $a, PHP_EOL;
    }

    private function foo2($a, $b)
    {
        echo $a + $b, PHP_EOL;
    }

    private function foo3($a, $b, $c)
    {
        echo $a + $b + $c, PHP_EOL;
    }

}

$c = new C();
$c->foo(1);
$c->foo(1, 2);
$c->foo(1, 2, 3);

使用__call()魔術方法或許會更簡單,但也會讓一些新手在接手項目的時候蒙圈。畢竟魔術方法對IDE是不友好的,這樣的開發讓__call()成爲了一個模板方法,由它來定義操做的算法骨架。咱們也能夠根據參數類型來模擬重載能力。學習

// 參數類型不一樣的重載
class D {
    function __call($name, $args){
        if($name == 'foo'){
            if(is_string($args[0])){
                $this->fooString($args[0]);
            }else {
                $this->fooInt($args[0]);
            }
        }
    }
    private function fooInt(int $a){
        echo $a . ' is Int', PHP_EOL;
    }

    private function fooString(string $a){
        echo $a . ' is String', PHP_EOL;
    }
}

$d = new D();
$d->foo(1);
$d->foo('1');

無論怎麼說,用上述方法實現的方法重載都很是麻煩,由於會讓某一個方法或者魔術方法很是重,它須要成爲一個控制器來根據參數對內部的方法進行調度。更多的狀況下,咱們應該仍是使用不一樣的方法名而後抽象公共的部分提取成獨立的私有內部方法來實現不一樣方法名的「重載」。畢竟不一樣的語言仍是要掌握它們不一樣的個性,而且根據這些個性靈活地運用在咱們的項目中。測試

測試代碼:
https://github.com/zhangyue0503/dev-blog/blob/master/php/201912/source/PHP%E4%B8%AD%E7%9A%84%E2%80%9C%E9%87%8D%E8%BD%BD%E2%80%9D%E6%98%AF%E4%B8%AA%E5%95%A5%EF%BC%9F.phpthis

參考文檔:
https://www.php.net/manual/zh/language.oop5.overloading.php#77843

===============

關注公衆號:【硬核項目經理】獲取最新文章

添加微信/QQ好友:【xiaoyuezigonggong/149844827】免費得PHP、項目管理學習資料

知乎、公衆號、抖音、頭條搜索【硬核項目經理】

B站ID:482780532

相關文章
相關標籤/搜索