本文来自pilishen.com----原文链接; 欢迎来和pilishen一起学习php&Laravel;学习群:109256050
该篇属于《Laravel底层核心技术实战揭秘》这一课程《laravel底层核心概念解析》这一章的扩展阅读。考虑到学员们的基础差异,为了避免视频当中过于详细而连篇累牍,故将一些laravel底层实现相关的PHP知识点以文章形式呈现,供大家预习和随时查阅。
在面向对象中,polymorphism(多态、多极对应)是基本而强大的工具,可以用它来更好地组织你的应用开发。
何谓Polymorphism?
Polymorphism describes a pattern in object oriented programming in which classes have different functionality while sharing a common interface. 多态,作为面向对象编程中的一种设计模式,指的是通过遵循同一个interface,类可以有不同的功能实现(相当于说,有多种形态)。
多态的一大魅力就是,我们不需要知道背后具体操作的是哪一个具体的class,虽然它们各自功能不同,只要它们都是实现了同一个interface,那么使用起来就相互没什么两样。
在物理现实中,我们可以把多态想象成“按按钮”——是个人就知道怎么去触碰一个按钮,尽管不同的按钮功能不一样,但只要它是个按钮,操作起来就非常简单。
同样的,在编程世界里,多态形式可以让我们的程序更加地模块化,易于扩展,而不是到处都是基于不同状态的条件判断,比如动不动就switch
,动不动就层层嵌套if
判断,这种情况下,十有八九是你的代码“有问题”了。
Interfaces(广义上的接口)
多态里面很关键的一环就是接口,广义上的接口包括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);
}
复制代码
Step 1: 面临的问题
假设你有一个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下,硬要去做太多的事情了。那么这个时候,也就是该尝试多态实现了。
Step 2: 定义 Interface
interface Writer {
public function write(Article $obj);
}
复制代码
Step 3: 具体实现 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只负责各自的那一件事。
Step 4: 具体调用
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() {
// 获取request参数
$format = $_REQUEST['format'];
// 形成相应的class name,并检查该class是否存在
$class = $format . 'Writer';
if(class_exists($class)) {
// 返回一个具体实例
return new $class();
}
// 否则的话抛出异常
throw new Exception('Unsupported format');
}
}
复制代码
Step 5: 终端调用
假设最终在controller里就可以这样来调用:
$article = new Article('Polymorphism', 'Steve', time(), 0);
try {
$writer = Factory::getWriter();
}
catch (Exception $e) {
$writer = new XMLWriter();
}
echo $article->write($writer);
复制代码
当然取决于你最终使用的框架,往往实际上比这个要更简单一些。
这里呢只是展示了多态的一种应用案例,供大家初步了解interface
的益处和必要性:interface
就像一个“蓝图”,基于这个“蓝图”,你可以放心地去实现多种形态的功能,从而使你的程序更加地模块化、易于扩展。
最后,可能我们更多关注的是在laravel里面,如何更好地面向interface,如何通过interface来提高多人团队开发效率,如何使我们更轻松地应对不断改变的功能需求,等等。关于这些,我们将继续在《Laravel底层核心技术实战揭秘》这一课程里深入探索。