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?”
In this context it’s useful to talk about interfaces.
Any animal or human (or alien) that implements the Talkative interface can be used in a context where talkative beings are needed:
This is a properly used polymorphic method. You don’t care what you’re dealing with as long as it can
speak().If
Dogs 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.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 callplayFetchbecause, 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:
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:Maybe down the road you’ll make a new breed of
GermanShepherds that are also awesome, butextendtheGermanShepherdclass. They’ll still work with theawesomenessfunction, 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.