看起来PHP中的多态性真的是多态吗?

时间:2009-04-14 23:30:10

标签: php oop programming-languages polymorphism

试图弄清楚PHP是否支持方法重载,继承和多态等功能,我发现:

  • 它不支持方法重载
  • 它支持继承

但我不确定多态性。我发现这是谷歌搜索互联网:

  

我应该注意在PHP中   多态性并不是它的方式   应该。我的意思是它确实有效,   但是因为我们的数据类型很弱,所以它   不正确。

它真的是多态吗?

修改 只是不能在PHP supports polymorphism旁边放置明确的“是”或“否”。我不愿意说:“PHP不支持多态”,而实际上它确实如此。反之亦然。

9 个答案:

答案 0 :(得分:32)

class Animal {
    var $name;
    function __construct($name) {
        $this->name = $name;
    }
}

class Dog extends Animal {
    function speak() {
        return "Woof, woof!";
    }
}

class Cat extends Animal {
    function speak() {
        return "Meow...";
    }
}

$animals = array(new Dog('Skip'), new Cat('Snowball'));

foreach($animals as $animal) {
    print $animal->name . " says: " . $animal->speak() . '<br>';
}

你可以随意标记它,但这对我来说就像多态。

答案 1 :(得分:16)

虽然PHP不支持方法重载你在其他语言中经历的方式,比如Java。但是你可以在PHP中使用方法重载,但定义方法是不同的。 如果你想为给定的方法提供不同的功能,在PHP中使用不同的参数集,你可以这样做:

class myClass {
    public function overloadedMethod() {
        // func_num_args() is a build-in function that returns an Integer.
        // the number of parameters passed to the method.
        if ( func_num_args() > 1 ) {
            $param1 = func_get_arg(0);
            $param2 = func_get_arg(1);
            $this->_overloadedMethodImplementation2($param1,$param2)
        } else {
            $param1 = func_get_arg(0);
            $this->_overloadedMethodImplementation1($param1)
        }
    }

    protected function _overloadedMethodImplementation1($param1) {
        // code 1
    }

    protected function _overloadedMethodImplementation2($param1,$param2) {
        // code 2
    }
}

可能会有更清晰的实施,但这只是一个样本。

PHP支持继承和接口。所以你可以使用它们进行多态性。你可以有这样的界面:

// file: MyBackupInterface.php
interface MyBackupInterface {
    // saves the data on a reliable storage
    public function saveData();
    public function setData();
}

// file: myBackupAbstract.php
require_once 'MyBackupInterface.php';
class MyBackupAbstract implements MyBackupInterface {
     protected $_data;
     public function setData($data) {
         $this->_data= $data;
     }
     // there is no abstract modifier in PHP. so le'ts avoid this class to be used in other ways
     public function __construct() {
          throw new Exception('this class is abstract. you can not instantiate it');
     }
}

// file: BackupToDisk.php
require_once 'MyBackupAbstract.php';
class BackupToDisk extends MyBackupAbstract {
     protected $_savePath;

     // implement other methods ...

     public function saveData() {
           // file_put_contents() is a built-in function to save a string into a file.
           file_put_contents($this->_savePath, $this->_data);
     }
}

// file: BackupToWebService.php
require_once 'MyBackupAbstract.php';
class BackupToWebService extends MyBackupAbstract {
     protected $_webService;
     // implement other methods ...

     public function saveData() {
           // suppose sendData() is implemented in the class
           $this->sendData($this->_data);
     }
}

现在在您的应用程序中,您可以像这样使用它:

// file: saveMyData.php
// some code to populate $myData
$backupSolutions = array( new BackupToDisk('/tmp/backup') , new BackupToWebService('webserviceURL') );
foreach ( $backupSolutions as $bs ) {
    $bs->setData($myData);
    $bs->saveData();
}

你是对的,PHP不是强类型语言,我们从未提到任何你的$ backupSolutions都是'MyBackupAbstract'或'MyBackupInterface',但这并不能阻止我们拥有不同功能的多态性使用相同的方法。

答案 2 :(得分:6)

__call()__callStatic()应该支持方法重载。有关详情,请参阅manual。或者你究竟追求的是什么?

更新:我刚刚注意到其他回复。

有关重载方法的另一种方法,请考虑以下事项:

<?php
public function foo()
{
    $args = func_get_arg();
}

当然不漂亮,但它可以让你做任何你想要的事情。

答案 3 :(得分:6)

PHP具有基于类的多态性,但缺乏实现基于参数的多态的正式机制。

基于类的多态意味着您可以根据基类进行思考,并且调用的方法取决于最终的类。例如,如果您有各种类的对象数组(如Triangle和Circle),并且这些类中的每一个都扩展了相同的类Shape,则可以将您的数组视为仅仅是形状的集合。您可以循环遍历形状并调用每个形状的getArea()方法。多态性是调用getArea()方法取决于对象类的现象。如果您的形状是三角形,则调用Triangle :: getArea(),如果是Circle,则调用Circle :: getArea() - 即使您的代码不区分Circle和Triangle但是将每个对象视为只是一个形状。相同的代码行导致执行不同的代码块,具体取决于对象的类。

基于参数的多态性是一些强类型语言的一个特性,其中同一个名称的多个方法可以在一个类中定义,前提是它们具有不同的参数;然后调用哪个方法取决于提供的参数。您可以通过手动考虑方法中的参数类型,在PHP等弱类型语言中模拟基于参数的多态。这是jQuery为了实现多态API而做的,尽管JavaScript缺乏基于本地参数的多态性。

因此,如果通过“支持多态”,你的意思是它提供了一种实现基于参数的多态的正式机制,答案是否定的。对于任何更广泛的解释,答案是肯定的。理所当然,基于类的多态性现象发生在每种面向对象的语言中;对于执行隐式类型转换以实现基于参数的多态的语言没有意义。

答案 4 :(得分:4)

你仍然可以覆盖方法,只是不要重载它们。重载(在C ++中)是您为多个方法使用相同的方法名称,仅在参数的数量和类型方面有所不同。这在PHP中很难,因为它是弱类型的。

覆盖是子类替换基类中的方法的位置。这实际上是多态性的基础,你可以在PHP中实现。

答案 5 :(得分:4)

有些人称之为duck typing

答案 6 :(得分:3)

PHP允许使用其他语言生成编译错误的多态代码。一个简单的例子说明了第一个产生预期编译错误的C ++代码:

class Base {};

class CommonDerivedBase {
   public:
    // The "= 0" makes the method and class abstract
    // virtual means polymorphic method
    virtual whoami() = 0;
};

class DerivedBase : public CommonDerivedBase {
   public:    
     void  whoami() { cout << "I am DerivedBase \n"; }
};

class Derived1 : public CommonDerivedBase {
  public:
    void  whoami() { cout << "I am Derived1\n"; }
};

class Derived2 : public CommonDerivedBase {
public:
 void whoami() { cout <<  "I am Derived2\n"; }
};

/* This will not compile */
void  test_error(Base& db)
{
   db.whoami();
}

C ++编译器将为行db.whoami()

发出此错误消息
error: no member named 'whoami' in 'Base'

因为Base没有名为whoami()的方法。但是,类似的PHP代码在运行时才会发现此类错误。

class Base {}

abstract class DerivedCommonBase {
  abstract function whoami();
}

class Derived1 extends DerivedCommonBase {

 public function whoami() { echo "I am Derived1\n"; }
}

class Derived2 extends DerivedCommonBase {

 public function whoami() { echo "I am Derived2\n"; }
}

/* In PHP, test(Base $b) does not give a runtime error, as long as the object 
 * passed at run time derives from Base and implements whoami().
 */
function test(Base $b)
{
  $b->whoami(); 
}

$b = new Base();
$d1 = new Derived1();
$d2 = new Derived2();

$a = array();

$a[] = $d1;
$a[] = $d2;

foreach($a as $x) {

  echo  test($x);
}

test($d1);
test($d2);
test($b); //<-- A run time error will result.

foreach循环与输出

一起使用
I am Derived1
I am Derived2

直到你调用test($ b)并传递一个Base实例才会出现运行时错误。所以在 foreach 之后,输出将是

I am Derived1
I am Derived2
PHP Fatal error:  Call to undefined method Base::whoami() in
home/kurt/public_html/spl/observer/test.php on line 22

关于使PHP更安全的唯一办法是添加运行时检查 测试$ b是否是你想要的类的实例。

function test(Base $b)
{
  if ($b instanceof DerivedCommonBase) {
     $b->whoami(); 
  } 
}

但多态性的全部意义在于消除此类运行时检查。

答案 7 :(得分:2)

多态性可以通过以下方法实现:

  1. method overriding - 正常漂亮如上

  2. method overloading

  3. 您可以通过魔术方法__call():

    创建方法重载的错觉
    class Poly {
        function __call($method, $arguments) {
            if ($method == 'edit') {
                if (count($arguments) == 1) {
    
                    return call_user_func_array(array($this,'edit1'), $arguments);
                } else if (count($arguments) == 2) {
                    return call_user_func_array(array($this,'edit2'), $arguments);
                }
            }
        }
        function edit1($x) {
            echo "edit with (1) parameter";
        }
    
        function edit2($x, $y) {
            echo "edit with (2) parameter";
        }
    
    }
    
    $profile = new Poly();
    $profile->edit(1);
    $profile->edit(1,2);
    

    Expln:

    1) Here we are utilizing the power of __call() of listening calls of 
       non-available     methods and 
    2) after knowing it who had called with their inputs diverting them to desired 
       method 
    

    In php,我们实际上是working under the hood来表达所需的行为并给予感受of method overloading

答案 8 :(得分:1)

对于我在这里看到的php,不支持多态,也不支持重载方法。你可以破解你真正接近这两种oop功能的方式,但它们远非它的最初目的。这里的许多例子都是扩展一个类或创建一个hack来扩散多态性。