Reputation: 1559
As only Dogs can play "fetch", is this example a good or a bad idea? I suspect it's a really bad idea due to the usage of instanceof, but I'm not entirely sure why.
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();
}
Another example. As I am constantly creating data objects that have an ID, I figured I might as well extend them all from a base class to avoid code duplication. Again, this was bad right? As a Chair doesn't have a name and a Dog doesn't have wheels. But they are both Data Objects so it's very confusing.
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;
}
}
Essentially what I think I'm asking is: "should all subclasses have the same interface or can they have different ones?"
Upvotes: 29
Views: 55247
Reputation: 6519
Another example for Polymorphism in 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);
?>
Upvotes: 29
Reputation: 418
Almost the same as you Krishnadas, Brad. This article helped me alot in understanding how to handle polymorphism in 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);
Upvotes: 3
Reputation: 522024
In this context it's useful to talk about interfaces.
interface Talkative {
public function speak();
}
class Dog extends Animal implements Talkative {
public function speak() {
return "Woof, woof!";
}
}
Any animal or human (or alien) that implements the Talkative interface can be used in a context where talkative beings are needed:
protected function makeItSpeak(Talkative $being) {
echo $being->speak();
}
This is a properly used polymorphic method. You don't care what you're dealing with as long as it can speak()
.
If Dog
s can also play fetch, that's great for them. If you want to generalize that, think about it in terms of an interface as well. Maybe one day you'll get a highly trained cat which can play fetch as well.
class Cog extends Cat implements Playfulness {
public function playFetch() { ... }
}
The important point here being that when you call playFetch()
on something, it's because you want to play fetch with that animal. You don't call playFetch
because, well... you can, but because you want to play fetch in this very moment. If you don't want to play fetch, then you don't call it. If you need to play fetch in a certain situation, then you need something that can play fetch. You ensure this through interface declarations.
You can achieve the same thing using class inheritance, it's just less flexible. In some situations where rigid hierarchies exist though it's perfectly useful:
abstract class Animal { }
abstract class Pet extends Animal { }
class Dog extends Pet {
public function playFetch() { ... }
}
class GermanShepherd extends Dog {
public function beAwesome() { ... }
}
Then, in some specific context, you may not require any object that can do something (interface), but you are specifically looking for a GermanShepherd
, because only it can be awesome:
protected function awesomeness(GermanShepherd $dog) {
$dog->beAwesome();
}
Maybe down the road you'll make a new breed of GermanShepherd
s that are also awesome, but extend
the GermanShepherd
class. They'll still work with the awesomeness
function, just like with interfaces.
What you certainly should not do is to loop through a bunch of random things, check what they are and make them do their own thing. That's just not very sensible in any context.
Upvotes: 49