Общий полиморфизм с примерами PHP
Как только собаки могут играть "извлечения", является ли этот пример хорошей или плохой идеей? Я подозреваю, что это действительно плохая идея из-за использования instanceof, но я не совсем уверен, почему.
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();
}
Другой пример. Поскольку я постоянно создаю объекты данных, у которых есть ID, я решил, что я мог бы также расширить их все из базового класса, чтобы избежать дублирования кода. Опять же, это было плохо? Поскольку у председателя нет имени, а у собаки нет колес. Но они являются обоими объектами данных, поэтому они очень запутывают.
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;
}
}
По сути, я думаю, что я спрашиваю: "должны ли все подклассы иметь один и тот же интерфейс или могут иметь разные?"
Ответы
Ответ 1
В этом контексте полезно говорить о интерфейсах.
interface Talkative {
public function speak();
}
class Dog extends Animal implements Talkative {
public function speak() {
return "Woof, woof!";
}
}
Любое животное или человек (или иностранец), реализующее интерфейс Talkative, может использоваться в контексте, в котором нужны болтливые существа:
protected function makeItSpeak(Talkative $being) {
echo $being->speak();
}
Это правильно используемый полиморфный метод. Вам все равно, что вы имеете в виду, если это возможно speak()
.
Если Dog
также может воспроизводить выборку, это здорово для них. Если вы хотите обобщить это, подумайте об этом и с точки зрения интерфейса. Возможно, однажды вы получите высококвалифицированного кота, который также может играть в выборку.
class Cog extends Cat implements Playfulness {
public function playFetch() { ... }
}
Важным моментом здесь является то, что когда вы вызываете playFetch()
на что-то, это потому, что вы хотите играть с этим животным. Вы не называете playFetch
, потому что, ну... вы можете, а потому, что вы хотите сыграть в выборку в этот самый момент. Если вы не хотите играть в выборку, вы не вызываете ее. Если вам нужно сыграть в выборку в определенной ситуации, вам нужно что-то, что может сыграть в выборку. Вы обеспечиваете это посредством деклараций интерфейса.
Вы можете добиться того же самого, используя наследование класса, оно менее гибкое. В некоторых ситуациях, когда существуют жесткие иерархии, хотя это совершенно полезно:
abstract class Animal { }
abstract class Pet extends Animal { }
class Dog extends Pet {
public function playFetch() { ... }
}
class GermanShepherd extends Dog {
public function beAwesome() { ... }
}
Затем в каком-то конкретном контексте вам может не потребоваться какой-либо объект, который может что-то сделать (интерфейс), но вы специально ищете GermanShepherd
, потому что только он может быть потрясающим:
protected function awesomeness(GermanShepherd $dog) {
$dog->beAwesome();
}
Возможно, по дороге вы создадите новую породу GermanShepherd
, которая также удивительна, но extend
класс GermanShepherd
. Они все равно будут работать с функцией awesomeness
, как с интерфейсами.
То, что вы, конечно же, не должны делать, - это перебрать кучу случайных вещей, проверить, что они собой представляют, и заставить их делать свое дело. Это просто не очень разумно в любом контексте.
Ответ 2
Еще один пример полиморфизма в 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);
?>
Ответ 3
Почти так же, как вы, Кришнадас, Брэд. Эта статья очень помогла мне понять, как обращаться с полиморфизмом в 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);