php多态
多态,作为面向对象编程中的一种设计模式,指的是通过遵循同一个interface,类可以有不同的功能实现(相当于说有多种形态)。
interface(广义上的接口),多态里面很关键的一环就是接口,广义上的接口包括interface 和 abstract class
接口和抽象类解释
interface
interface里可以定义方法名及相应参数,任何实现这个interface的类必须具体实现interface里定义的所有抽象方法,一个class可以实现多个interface。
interface MyInterface {
public function doThis();
public function doThat();
public function setName($name);
}
// 正确的做法
class MyClass implements MyInterface {
protected $name;
public function doThis() {
// code that does this
}
public function doThat() {
// code that does that
}
public function setName($name) {
$this->name = $name;
}
}
// 无效的做法
class MyClass implements MyInterface {
// 缺少 doThis()方法!
private function doThat() {
// 这个方法必须也是public!
}
public function setName() {
// 缺少 name 参数!
}
}
abstract class
abstract class 可以说是介于interface 和普通class之间,它既可以通过abstract method的形式定义统一的接口,又可以定义具体的实现。一个扩展了改abstract class 的普通class ,必须具体实现该abstract class的所有抽象方法。
abstract class MyAbstract {
public $name;
public function doThis() {
// do this
}
abstract public function doThat();
abstract public function setName($name);
}
多态实现
假设你有一个article class
class Article {
public $title;
public $author;
public $date;
public $category;
public function __construct($title, $author, $date, $category = 0) {
$this->title = $title;
$this->author = $author;
$this->date = $date;
$this->category = $category;
}
}
现在呢,你想添加一个方法,来以不同的形式输出article相关的信息,比如说XML格式,或者说JSON格式。
可能我们一开始会想着这么来处理:
class Article {
//...
public function write($type) {
$ret = '';
switch($type) {
case 'XML':
$ret = '<article>';
$ret .= '<title>' . $obj->title . '</title>';
$ret .= '<author>' . $obj->author . '</author>';
$ret .= '<date>' . $obj->date . '</date>';
$ret .= '<category>' . $obj->category . '</category>';
$ret .= '</article>';
break;
case 'JSON':
$array = array('article' => $obj);
$ret = json_encode($array);
break;
}
return $ret;
}
}
虽然功能上能实现效果,但是看上去很糟糕,不是吗?假设,将来你又想加上其他的格式,那该怎么办?再加几个case判断,这代码得多臃肿呢?
关于面向对象,有一个很重要的原则就是,一个class应该只做份内之事。每当你遇到大块的条件判断的时候,你就应该有所警醒,因为很可能这个时候你已经在同一个class或method下,硬要去做太多的事情了。那么这个时候,也就是该尝试多态实现了。
01 首先定义一个interface
interface Writer {
public function write(Article $obj);
}
02 具体实现interface
XMLWriter可以这样来实现:
class XMLWriter implements Writer {
public function write(Article $obj) {
$ret = '<article>';
$ret .= '<title>' . $obj->title . '</title>';
$ret .= '<author>' . $obj->author . '</author>';
$ret .= '<date>' . $obj->date . '</date>';
$ret .= '<category>' . $obj->category . '</category>';
$ret .= '</article>';
return $ret;
}
}
JSONWriter:
class JSONWriter implements Writer {
public function write(Article $obj) {
$array = array('article' => $obj);
return json_encode($array);
}
}
这样每一个class只负责各自的那一件事。
03 具体调用
class Article {
//...
public function write(Writer $writer) {
return $writer->write($this);
}
}
这样article的write方法接收的是一个实现了Writer这个interface的具体类,article不再需要关注具体该用什么样的格式,那已经不是它要负责的了,交给背后具体的Writer去处理就好了。
至于怎么传一个具体的writer进去,这个就取决于你的使用情境了,比如你可以用一个factory class来这样操作:
class Factory {
public static function getWriter($format) {
// 形成相应的class name,并检查该class是否存在
$class = $format . 'Writer';
if(class_exists($class)) {
// 返回一个具体实例
return new $class();
}
// 否则的话抛出异常
throw new Exception('Unsupported format');
}
}
04 终端调用
$article = new Article();
try {
$writer = Factory::getWriter('json');
}
catch (Exception $e) {
$writer = new XMLWriter();
}
echo $article->write($writer);
这里呢只是展示了多态的一种应用案例,供大家初步了解interface的益处和必要性:interface就像一个“蓝图”,基于这个“蓝图”,你可以放心地去实现多种形态的功能,从而使你的程序更加地模块化、易于扩展。