理解和运用PHP中的多态性[译]


Posted in PHP onAugust 02, 2011

什么是多态性?
Polymorphism(多态性)是一个很长的单词,但是它表示的是一个非常简单的概念。
多态性描述了在面向对象编程模式中类有不同的功能,而共享一个通用的接口。
多态性的优点是,并不需要知道它使用的是哪一个类,因为他们都用同样的方式与不同的类的代码工作。
可将多态性类比成现实世界的一个按钮。大家都知道如何使用一个按钮:您只需给它施加压力。一个按钮“确实是这样”,然而,取决于它和什么连接和使用它的上下文 - 但结果并不影响它是如何使用。如果你的老板告诉你按下一个按钮,您已经有执行任务所需的所有信息。
在编程的世界中,多态性是用来使应用程序更加模块化和可扩展的。相比于凌乱的条件语句描述的行动不同的课程,您可以创建根据您的需求选择的互换对象。这是多态性的基本目标。
Interfaces接口
接口是与类(class)类似的,除了它不能包含代码。接口可以定义方法名称和参数,但不是方法的内容。实现接口的任何类都必须实现接口中定义的所有方法。一个类可以实现多个接口。
使用的“interface”关键字声明一个接口:

interface MyInterface { 
// methods 
}

被附加到一个类,使用“implements”关键字(多个接口,可以通过用逗号分开):
class MyClass implements MyInterface { 
// methods 
}

在接口中可以像在类中一样定义方法,除了没有方法体(在大括号中的部分)以外。
interface MyInterface { 
public function doThis(); 
public function doThat(); 
public function setName($name); 
}

在这里定义的所有方法都必须如接口中所描述地那样被包含在任何实现它的类中。(读下面的代码注释)
//合法的 VALID 
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; 
} 
} 
// 非法的INVALID 
class MyClass implements MyInterface { 
// missing doThis()! 
private function doThat() { 
// this should be public! 
} 
public function setName() { 
// missing the name argument! 
} 
}

抽象类Abstract Class
抽象类是接口和类的混合。它可以像接口一样定义方法。继承自抽象类的类必须实现抽象类中定义的所有抽象方法。
抽象类的定义方式与类一样,不过是在前面附加了一个abstract 关键字。
abstract class MyAbstract { 
// methods 
}

并且 是用 ‘extends‘ 关键字附加到类:
class MyClass extends MyAbstract { 
// class methods 
}

就像在普通类中一样,普通的方法以及任何抽象方法(使用关键字“abstract”)可以在抽象类中定义。抽象方法的行为就像在接口中定义的的方法,而且继承它的扩展类中必须实现完全一样的定义。
abstract class MyAbstract { 
public $name; 
public function doThis() { 
// do this 
} 
abstract public function doThat(); 
abstract public function setName($name); 
}

我们假设你有一个文章Article类负责管理你网站上的文章。它包含关于文章的信息,包括:title, author, date, and category.
就像下面这样:
class poly_base_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; 
} 
}

注意:在这个教程中的示例类使用了“package_component_Class”的命名约定,这是一个用来将类名分隔到虚拟的命名空间来避免命名冲突的通用方法。
现在你想添加一个方法来输出各种不同格式的信息,如XML和JSON。你也许会打算像下面这样做:
class poly_base_Article { 
//... 
public function write($type) { 
$ret = ''; 
switch($type) { 
case 'XML': 
$ret = ''; 
$ret .= ''; 
$ret .= '' . $obj->author . ''; 
$ret .= '' . $obj->date . ''; 
$ret .= '' . $obj->category . ''; 
$ret .= ''; 
break; 
case 'JSON': 
$array = array('article' => $obj); 
$ret = json_encode($array); 
break; 
} 
return $ret; 
} 
}

这种解决方案是丑陋的,但是它是可靠的——至少在现在。问下你自己将来会发生什么,当我们需要添加更多格式的时候?你可以继续编辑这个类,然后添加越来越多的case ,
但是现在你只是在稀释(FIX ME: diluting) 你的类。
OOP的一个重要原则是一个类应该做的一件事情,而应该把它做好。
考虑到这一点,条件语句应该是一个红色的标志,表明你的类是试图做太多不同的东西。这是多态性的用武之地。
在我们的例子中,有两个任务明确的提出:管理文章和格式化其数据。在本教程中,我们将重构我们的格式化代码到一个新的类,然后我们会发现使用多态性是多么容易。
Step 2: 定义你的接口 Define Your Interface
第一件事就是我们应该定义接口,努力想好怎么定义你的接口是件重要的事情,因为对它的任何改变都将需要改动调用它的代码。
在我们这个例子中,我们将使用一个简单的接口来定义一个方法:
interface poly_writer_Writer { 
public function write(poly_base_Article $obj); 
}

它就是那样简单,我们已经定义了一个公用方法write() ,它接受一个文章对象作为参数。任何实现Writer接口的类都将会确保有这个方法。
小提示:如果你想严格限制传递给你的方法和函数的参数类型,你可以使用类型提示,就像我们已经在write()方法中做的一样,它只能接受poly_base_Article 对象类型的数
据。不幸的是,在目前版本的PHP中,返回类型提示是不被支持的,所以,你要小心返回值的类型了。
Step 3: 创建实现类 Create Your Implementation
定义接口后,该是时候来创建类来真正干活的啦。在我们的例子中,我们需要输出两种格式。这样,我们就要两个Writer 类:XMLWriter 和 JSONWriter 。从传递过来的
Article 对象提取数据然后格式化这些信息完全取决于这些类了。
下面是 XMLWriter 类的一个示例:
class poly_writer_XMLWriter implements poly_writer_Writer { 
public function write(poly_base_Article $obj) { 
$ret = ''; 
$ret .= ''; 
$ret .= '' . $obj->author . ''; 
$ret .= '' . $obj->date . ''; 
$ret .= '' . $obj->category . ''; 
$ret .= ''; 
return $ret; 
} 
}

就如你可以从类定义中看到的一样,我们使用implements关键字来实现我们的接口。write() 方法包含格式化为XML的功能。
现在我们来看下JSONWriter 类:
class poly_writer_JSONWriter implements poly_writer_Writer { 
public function write(poly_base_Article $obj) { 
$array = array('article' => $obj); 
return json_encode($array); 
} 
}

现在,我们的代码中的特定每种格式都包含在单独的类。每个类有全权负责处理特定的格式,而不是其他。您的应用程序中没有其他部分需要关心这些是如何工作的才能使用它,
感谢我们的接口。
Step 4: 使用你的接口Use Your Implementation
在我们的新类定义后,该是时候来重温一下我们的Article类了,所有原write() 方法中的代码已经被分离出来,进入到我们的新类中了。
我们的所有方法现在需要做的就是使用这些新的类,像这样:
class poly_base_Article { 
//... 
public function write(poly_writer_Writer $writer) { 
return $writer->write($this); 
} 
}

获取一个 Writer对象 Obtaining A Writer
你也许会疑惑你该从哪里获取一个 Writer对象开始,因为你需要传递一个 Writer对象到这个方法。
这完全取决于你,并且,有很多策略。如,你可能会使用工厂类来获取请求数据然后创建一个对象:
class poly_base_Factory { 
public static function getWriter() { 
// grab request variable 
$format = $_REQUEST['format']; 
// construct our class name and check its existence 
$class = 'poly_writer_' . $format . 'Writer'; 
if(class_exists($class)) { 
// return a new Writer object 
return new $class(); 
} 
// otherwise we fail 
throw new Exception('Unsupported format'); 
} 
}

就像我说的,根据你的需求,有好多其它策略可用。在这个例子中,通过一个请求变量选择哪种格式是要使用的。它基于request请求变量来构造一个类名,检测它是否存在,
然后返回一个新的Writer对象。如果没有那个名字的类存在,抛出一个异常,让客户端代码决定接下来要干什么。
Step 5: 把它们放一起 Put It All Together
当所有东东都到位了,下面是我们的客户端代码如何放在一起:
$article = new poly_base_Article('Polymorphism', 'Steve', time(), 0); 
try { 
$writer = poly_base_Factory::getWriter(); 
} 
catch (Exception $e) { 
$writer = new poly_writer_XMLWriter(); 
} 
echo $article->write($writer);

首先,我们创建了一个示例 Article 对象来配合工作。然后,我们试图从工厂Factory获取一个Factory对象,如果异常发生的话回滚到默认(XMLWriter) 。
最后,我们传递Writer对象给我们的Article的 write() 方法,输出结果。
结论 Conclusion 在本教程中,我提供了一个多态性的简介而且解释了PHP中的接口。我希望你意识到,我只向您展示一个潜在的使用多态性的案例。
多态性是以一个优雅的方式来避免您的OOP代码中丑陋的条件语句。它遵循的原则是使您的组件分离,而且它是许多设计模式的组成部分。如果您有任何问题,不要犹豫,在评论中提问!
译自:http://net.tutsplus.com/tutorials/php/understanding-and-applying-polymorphism-in-php/
原文发表在:http://ihacklog.com/?p=4703
PHP 相关文章推荐
php 页面执行时间计算代码
Dec 04 PHP
深入php数据采集的详解
Jun 02 PHP
PHP用星号隐藏部份用户名、身份证、IP、手机号等实例
Apr 08 PHP
PHP英文字母大小写转换函数小结
May 03 PHP
php使用pdo连接mssql server数据库实例
Dec 25 PHP
Smarty模板简单配置与使用方法示例
May 23 PHP
PHP+Ajax实现验证码的实时验证
Jul 20 PHP
Zend Framework上传文件重命名的实现方法
Nov 25 PHP
关于php几种字符串连接的效率比较(详解)
Feb 22 PHP
Django中的cookie与session操作实例代码
Aug 17 PHP
PHP开发之用微信远程遥控服务器
Jan 25 PHP
PHP文件上传小程序 适合初学者学习!
May 23 PHP
应用开发中涉及到的css和php笔记分享
Aug 02 #PHP
PHP源代码数组统计count分析
Aug 02 #PHP
linux下为php添加curl扩展的方法
Jul 29 #PHP
php中修改浏览器的User-Agent来伪装你的浏览器和操作系统
Jul 29 #PHP
php 判断访客是否为搜索引擎蜘蛛的函数代码
Jul 29 #PHP
php.ini中date.timezone设置分析
Jul 29 #PHP
PHP调用Webservice实例代码
Jul 29 #PHP
You might like
php生成数组的使用示例 php全组合算法
2014/01/16 PHP
PHP实现的Redis多库选择功能单例类
2017/07/27 PHP
php获取微信共享收货地址的方法
2017/12/21 PHP
javascript中的undefined 与 null 的区别  补充篇
2010/03/17 Javascript
javascript preload&lazy load
2010/05/13 Javascript
js禁止页面使用右键(简单示例代码)
2013/11/13 Javascript
Node.js(安装,启动,测试)
2014/06/09 Javascript
jQuery制作效果超棒的手风琴折叠菜单
2015/04/03 Javascript
JS表格组件神器bootstrap table详解(基础版)
2015/12/08 Javascript
如何使用headjs来管理和异步加载js
2016/11/29 Javascript
JS实现给对象动态添加属性的方法
2017/01/05 Javascript
基于vue.js快速搭建图书管理平台
2017/10/29 Javascript
vue 地图可视化 maptalks 篇实例代码详解
2019/05/21 Javascript
防止Layui form表单重复提交的实现方法
2019/09/10 Javascript
[01:27]DOTA2电竞之夜 今夜共饮庆功酒
2014/08/02 DOTA
在Python下尝试多线程编程
2015/04/28 Python
详解Python设计模式编程中观察者模式与策略模式的运用
2016/03/02 Python
Python实现递归遍历文件夹并删除文件
2016/04/18 Python
Python实现句子翻译功能
2017/11/14 Python
pytorch 调整某一维度数据顺序的方法
2018/12/08 Python
Python使用统计函数绘制简单图形实例代码
2019/05/15 Python
python函数的万能参数传参详解
2019/07/26 Python
详解python算法常用技巧与内置库
2020/10/17 Python
用CSS3将你的设计带入下个高度
2009/08/08 HTML / CSS
Aurora London官网:奢华、负担得起的皮革手袋
2020/08/01 全球购物
如何减少垃圾回收让内存更加有效使用
2013/10/18 面试题
优秀广告词大全
2014/03/19 职场文书
五四青年节优秀演讲稿范文
2014/05/28 职场文书
解除聘用合同证明书范本
2014/09/11 职场文书
村党支部书记个人对照材料汇报
2014/10/26 职场文书
2014年驻村干部工作总结
2014/11/17 职场文书
简单的辞职信范文(2016最新版)
2015/05/12 职场文书
2015年支教教师工作总结
2015/07/22 职场文书
幼儿园科学课教学反思
2016/03/03 职场文书
linux下导入、导出mysql数据库命令的实现方法
2021/05/26 MySQL
正则表达式拆分url实例代码
2022/02/24 Java/Android