2011-12-17 10 views
25

Como solo los perros pueden jugar "fetch", ¿es este un buen o una mala idea? Sospecho que es una muy mala idea debido al uso de instanceof, pero no estoy del todo seguro de por qué.Polimorfismo general con ejemplos de PHP

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

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

    function playFetch() { 
     return 'getting the stick'; 
    } 
} 

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>'; 
    if ($animal instanceof Dog) echo $animal->playFetch(); 
} 

Otro ejemplo. Como estoy constantemente creando objetos de datos que tienen una ID, pensé que también podría extenderlos a todos desde una clase base para evitar la duplicación de código. De nuevo, esto estuvo mal ¿no? Como una silla no tiene nombre y un perro no tiene ruedas. Pero ellos son ambos objetos de datos por lo que es muy confuso.

class Data_Object { 
    protected $_id; 

    function setId($id) { 
     $this->_id = $id; 
    } 

    function getId() { 
     return $this->_id; 
    } 
} 

class Dog extends Data_Object { 
    protected $_name; 
    function setName($name) { 
     $this->_name = 
    } 

    function getName() { 
     return $this->_name; 
    } 
} 

class Chair extends Data_Object { 
    protected $_numberOfWheels; 
    function setNumberOfWheels($number) { 
     $this->_numberOfWheels = $number; 
    } 

    function getNumberOfWheels() { 
     return $this->_numberOfWheels; 
    } 
} 

Esencialmente lo que yo creo que estoy pidiendo es: "todas las subclases deben tener la misma interfaz o pueden tener otros diferentes?"

+2

bastante seguro de que se ha pedido aquí antes, pero la respuesta corta es que está bien que a subclases tienen una interfaz diferente. – NullUserException

+0

Estoy bastante seguro de que el primer ejemplo no es correcto debido al uso de instanceof para la lógica condicional. ¿No es el objetivo del Polimorfismo no hacer esto? – Dan

+0

¿Por qué querrías hacer que todos los animales hicieran algo (hablar), excepto un tipo (perro)? Parece que la lógica defectuosa está en el bucle, no las definiciones de clase. – NullUserException

Respuesta

44

En este contexto, es útil hablar de las interfaces .

interface Talkative { 
    public function speak(); 
} 

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

Cualquier animal o humano (o extranjero) que implementa la interfaz Talkative se puede utilizar en un contexto en que se necesitan los seres hablador:

protected function makeItSpeak(Talkative $being) { 
    echo $being->speak(); 
} 

Este es un método polimórfico se utiliza correctamente. No le importa con qué está tratando, siempre que pueda speak().

Si Dog s también pueden jugar a buscar, eso es genial para ellos. Si desea generalizar eso, piense en términos de una interfaz también. Tal vez algún día consigas un gato altamente entrenado que también pueda jugar a buscar.

class Cog extends Cat implements Playfulness { 
    public function playFetch() { ... } 
} 

El punto importante aquí es que cuando llamadaplayFetch() en algo, es porque quiere jugar a la pelota con ese animal. No llamas al playFetch porque, bueno ... puedes, pero porque quieres jugar a buscar en este mismo momento. Si no quieres jugar a buscar, entonces no lo llamas. Si necesitas jugar a buscar en una situación determinada, entonces necesitas algo que pueda jugar a buscar. Usted asegura esto a través de declaraciones de interfaz.

Puede lograr lo mismo utilizando la herencia de clase, es simplemente menos flexible. En algunas situaciones en las que existen jerarquías rígidas pesar de que es perfectamente útil:

abstract class Animal { } 

abstract class Pet extends Animal { } 

class Dog extends Pet { 
    public function playFetch() { ... } 
} 

class GermanShepherd extends Dog { 
    public function beAwesome() { ... } 
} 

Luego, en un contexto específico, es posible que no requieren cualquier objeto que puede hacer algo (interfaz), pero usted está buscando específicamente para un GermanShepherd , debido que puede ser impresionante:

protected function awesomeness(GermanShepherd $dog) { 
    $dog->beAwesome(); 
} 

Tal vez por el camino que va a hacer una nueva generación de GermanShepherd s que también son impresionantes, pero extend la clase GermanShepherd. Seguirán funcionando con la función awesomeness, al igual que con las interfaces.

Lo que no debes hacer es pasar por un montón de cosas al azar, verificar cuáles son y hacer que hagan lo suyo.Eso no es muy sensato en ningún contexto.

+5

es refrescante ver a un desarrollador de php hablar sobre interfaces. Los uso mucho en php, pero en la mayoría de los frameworks principales no hay amor, y por lo tanto la adopción de la ideología es muy baja por parte del desarrollador de php común. –

+0

@deceze Gran respuesta. Pero me pregunto si los rasgos también valen la pena mencionar aquí. http://www.php.net/manual/en/language.oop5.traits.php –

+0

@Muhammad No realmente, IMO. Los rasgos no son de tipo testable y, por lo tanto, son algo irrelevantes para la discusión. – deceze

22

Otro ejemplo de polimorfismo en PHP

<?php 
interface Shape { 
    public function getArea(); 
} 

class Square implements Shape { 
    private $width; 
    private $height; 

    public function __construct($width, $height) { 
     $this->width = $width; 
     $this->height = $height; 
    } 

    public function getArea(){ 
     return $this->width * $this->height; 
    } 
} 

class Circle implements Shape { 
    private $radius; 

    public function __construct($radius) { 
     $this->radius = $radius; 
    } 

    public function getArea(){ 

     return 3.14 * $this->radius * $this->radius; 
    } 
} 

function calculateArea(Shape $shape) { 
    return $shape->getArea(); 
} 

$square = new Square(5, 5); 
$circle = new Circle(7); 

echo calculateArea($square), "<br/>"; 
echo calculateArea($circle); 
?> 
2

Casi lo mismo que usted Krshnadas, Brad. Este artículo me ha ayudado mucho en la comprensión de cómo manejar el polimorfismo en PHP

http://code.tutsplus.com/tutorials/understanding-and-applying-polymorphism-in-php--net-14362

interface shape_drawer{ 
    public function draw(Shape $obj); 
} 

class circle implements shape_drawer{ 
    public function draw(Shape $obj){ 
     echo "Drawing Circle, Area: {$obj->area} <br>"; 
    } 
} 

class square implements shape_drawer{ 
    public function draw(Shape $obj){ 
     echo "Drawing Square, Area: {$obj->area} <br>"; 
    } 
} 

class triangle implements shape_drawer{ 
    public function draw(Shape $obj){ 
     echo "Drawing Triangle, Area: {$obj->area} <br>"; 
    }  
} 

class shape_factory{ 
    public static function getShape(){ 

     $shape = $_REQUEST['shape']; 

     if(class_exists($shape)) { 
      return new $shape(); 
     } 
     throw new Exception('Unsupported format'); 
    } 
} 

class Shape{ 

    public function __construct($area){ 
     $this->area = $area; 
    } 
    public function draw(shape_drawer $obj) { 
     return $obj->draw($this); 
    } 
} 


$shape = new Shape(50); 
try { 
    $drawer = shape_factory::getShape(); 
} 
catch (Exception $e) { 
    $drawer = new circle(); 
} 

echo $shape->draw($drawer);