Является ли полиморфизм в PHP полиморфизмом? - PullRequest
22 голосов
/ 15 апреля 2009

Пытаясь выяснить, поддерживает ли PHP такие функции, как перегрузка методов, наследование и полиморфизм, я обнаружил:

  • не поддерживает перегрузку метода
  • поддерживает наследование

но я не уверен в полиморфизме. Я нашел это Гугля в интернете:

Я должен отметить, что в PHP полиморфизм не совсем так должно быть. Я имею в виду, что это работает, но так как у нас слабый тип данных, его не правильно.

Так это действительно полиморфизм?

Редактировать Просто не могу поставить определенное ДА или НЕТ рядом с PHP supports polymorphism. Я не хотел бы утверждать: «PHP не поддерживает полиморфизм», хотя на самом деле это так. Или наоборот.

Ответы [ 9 ]

32 голосов
/ 15 апреля 2009
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>';
}

Вы можете пометить это как хотите, но для меня это похоже на полиморфизм.

16 голосов
/ 15 апреля 2009

хотя 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, но это не помешает нам иметь природу полиморфизма, который отличается функциональностью используя те же методы.

6 голосов
/ 27 декабря 2013

PHP имеет полиморфизм на основе классов, но не имеет формального механизма реализации полиморфизма на основе аргументов.

Полиморфизм на основе классов означает, что вы можете мыслить в терминах базового класса, а вызываемые методы зависят от конечного класса. Например, если у вас есть массив объектов различных классов, таких как треугольник и круг, и каждый из этих классов расширяет один и тот же класс Shape, вы можете рассматривать свой массив как просто набор фигур. Вы можете перебирать фигуры и вызывать метод getArea () каждой фигуры. Полиморфизм - это явление, при котором вызываемый метод getArea () зависит от класса объекта. Если ваша фигура представляет собой треугольник, вызывается Triangle :: getArea (), если вызывается Circle, то вызывается Circle :: getArea () - даже если ваш код не различает круг и треугольник, а рассматривает каждый объект как просто форма. Одна и та же строка кода приводит к выполнению другого блока кода в зависимости от класса объекта.

Полиморфизм на основе аргументов - это особенность некоторых строго типизированных языков, в которых несколько методов с одним и тем же именем могут быть определены в одном классе, при условии, что они имеют разные параметры; то, какой метод вызывается, зависит от предоставленных аргументов. Вы можете эмулировать основанный на аргументах полиморфизм в слабо типизированных языках, таких как PHP, вручную рассматривая типы аргументов в своем методе. Это то, что делает jQuery для реализации полиморфного API, несмотря на отсутствие в JavaScript собственного полиморфизма на основе аргументов.

Таким образом, если под «поддержкой полиморфизма» вы подразумеваете конкретно, что он предоставляет формальный механизм для реализации основанного на аргументах полиморфизма, ответ - нет. Для любой более широкой интерпретации ответ - да. Само собой разумеется, что явление полиморфизма на основе классов встречается в каждом объектно-ориентированном языке; и это не имеет смысла для языка, который выполняет неявное преобразование типов для реализации основанного на аргументах полиморфизма.

6 голосов
/ 15 апреля 2009

__call() и __callStatic() должны поддерживать перегрузку метода. Подробнее об этом можно прочитать в руководстве . Или что именно вы после?

ОБНОВЛЕНИЕ: Я только что заметил другие ответы.

Для другого способа перегрузки метода рассмотрим следующее:

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

Конечно, не очень, но это позволяет вам делать практически все, что вы хотите.

4 голосов
/ 15 апреля 2009

Некоторые называют это Утка набрав .

4 голосов
/ 15 апреля 2009

Вы все еще можете переопределить методы, но не перегружайте их. Перегрузка (в C ++) - это использование одного и того же имени метода для нескольких методов, различающихся только количеством и типами параметров. Это было бы сложно в PHP, так как оно слабо типизировано.

Переопределение - это когда подкласс заменяет метод в базовом классе. Что действительно является основой полиморфизма, и вы можете сделать это в PHP.

3 голосов
/ 25 февраля 2014

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(); 
  } 
}

Но весь смысл полиморфизма в том, чтобы исключить такие проверки во время выполнения.

2 голосов
/ 02 апреля 2013

Полиморфизм может быть реализован следующими способами:

  1. method overriding - нормальная симпатичная была как выше

  2. method overloading

Вы можете создать иллюзию перегрузки метода с помощью магического метода __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

1 голос
/ 31 мая 2012

Для того, что я видел здесь, php не поддерживает ни полиморфизм, ни методы перегрузки. Вы можете взломать свой путь, чтобы фактически приблизиться к обоим из этих основных функций, но они далеки от первоначальной цели этого. Многие из приведенных здесь примеров либо расширяют класс, либо создают хак для эмуляции полиморфизма.

...