PHP经典问答

2007-8-28  05:50

问题:什么情况下继承是错误的?

答案:假设一个CMS系统,文章类都从数据库类继承,这样文章类就自然而然的得到了数据库的操作方法,例子代码如下:

PHP代码
  1. class DB {
  2. function get(…) {}
  3. }
  4. class Article extends DB {
  5. function find(…) {
  6. return $this->get(…);
  7. }
  8. }

这是最典型的继承滥用。因为对一个文章类和一个数据库类而言,他们之间不存在父子关系,所以在这里使用继承是不必要的,而应该使用委派。

PHP代码
  1. class Article {
  2. var $db;
  3. function Article($db) {
  4. $this->db = $db;
  5. }
  6. function find(…) {
  7. return $this->db->get(…);
  8. }
  9. }

如果你从来没有想用子类的某个方法去覆盖父类的某个方法的实现的话,那你就要注意了,你的继承关系很可能是有问题的,过多的把继承作为代码复用的方法说一种技术思维,因为很多情况下,你用委派也能达到一样的效果。

对单继承语言来说,继承犹为宝贵,不可滥用,只有逻辑上有相应关系才可以使用继承,不要试图从技术角度去决定是否使用继承。

问题:if/else是邪恶的么?

答案:这个问题要一分为二的看待。先看演示代码,如下:

 

PHP代码

     

  1. class Toilet {
  2. function go($person) {
  3. if ($person == ‘man’) {
  4. // 去男厕
  5. } elseif ($person == ‘woman’) {
  6. // 去女厕
  7. } else {
  8. // 去医院
  9. }
  10. }
  11. }

 

在此种情况下的if/else使用,多半是错误的,因为这里涉及一个最基本的OOP概念:多态。让我们来看看改进的代码:

 

PHP代码

  1. class Person
  2. {
  3. function goWC() {
  4. // 去医院
  5. }
  6. }
  7. class Man extends Person {
  8. function goWC() {
  9. // 去男厕
  10. }
  11. }
  12. class Woman extends Person {
  13. function goWC() {
  14. // 去女厕
  15. }
  16. }
  17. class Toilet {
  18. function go($person) {
  19. $person->goWC();
  20. }
  21. }

很明显,我们可以发现利用多态消除了if/else,提高的程序的可读性。

对于初学者来说,如果套用强类型语言的多态思想去理解弱类型语言的多态,有时候反到会想不懂,其实换个角度看,对于弱类型语言来说,多态支持是与生俱来的,不要把它想得复杂了。

并不是所有的if/else都是邪恶的,对于一些类似工作流的判断语句而言,他们本身是再正常不过的了,比如说:if 迟到了,then 扣十块钱 等等,这些判断语句使用if/else来做是完全没有问题的,那么如何把握判断的尺度呢?介绍一个简单的方法:在 if “什么” else “什么”这样的的结构中,如果“什么”是名词的话,多半可以利用多态消除这样的if/else,如果是形容词的话,则大都是正常的判断语句。

当然了,理解多态是最要紧的,这也是OOP最精华的部分。

问题:如何提高程序的可测性?

答案:提高程序的可测试性需要很多方面的知识,这里说最常见的一条,先看演示代码:

 

PHP代码

  1. class Foo {
  2. function bar() {
  3. require ‘obj.php’;
  4. $obj = new obj();
  5. $obj->someFunction();
  6. }
  7. }

这样的代码在现在的PHPER里很常见,不过老实说,这样的代码基本没有可测试性,因为方法里引入了第三方因素,没法隔离,下面看看改进的代码:

 

PHP代码

     

  1. class Foo {
  2. function bar($obj) {
  3. $obj->someFunction();
  4. }
  5. }

 

这样,就具有了可测试性,至于所需代码的注入,有很多方法,比如构造器注入,setter注入等等,做了这样的改变后,我们在测试的时候就可以把$obj整体Mock掉,保证测试中无关因素的隔离。

类似的道理,在类中大量使用static方式的静态方法调用也不是太好的选择,因为static方法没有办法Mock掉,当然,也不尽然,比如说,对于单态模式而言,使用static是很正常的事情。

问题:如何运用MVC?

答案:MVC这个名词在PHP社区里现在差不多是妇孺皆知了,但老实说,真正能写出优美的MVC代码的程序员不多,即便是现在已经有了很多不错MVC框架(CakePHP,ZendFramework等),这样的情况依然没有本质的改观。

MVC强调两个分离:最重要的分离是M和V的分离,其次是V和C的分离。

M和V的分离的必要性是显而易见的,比如说同一段统计数据,我们既想通过表格展示,又想通过GD画饼图表示,在类似这样的情况下,M和V的分离使得一个M可以同时服务于多个V。这个分离的关键点在与V依赖于M,而M不依赖于V。

但有很多程序员不能很清晰的区分这样的分离,在M中掺杂了和V相关的代码,产生了坏味道:

在文章列表页显示文章标题,每个标题显示前十个字,多于的部分截断,并在后面显示..,很多PHPer程序员在处理这样问题的时候,都是在M的方法 里完成了相应的截取工作,然后交给视图。但是到底显示几个字很明显是视图的职责,同样的数据,在这里可能显示前十个字,在那里可能就只能显示前五个字,所 以,对M而言,不要考虑和显示细节相关的逻辑,至于显示几个字这样的视图逻辑,可以交给视图助手去处理,比如Smarty当中的truncate。

V和C的分离实际并不是很重要,对于一个web应用而言,V和C大多数情况下都是一一对应的关系。

MVC实质上是一个表现层架构模式,它的名字中虽然含有M,但是它本身并没有涉及如何建造M,大多数MVC框架的实现更像是一个复杂的C实现,这有 时候误导了使用者,导致他们对待逻辑的时候不加区分(领域逻辑,应用逻辑),统统放在C中。从而导致了一个贫血的模型,M也就成为了摆设,甚至沦为数据容 器,这样的所谓面向对象实质和面向过程没有区别,是一种伪OO。

如果要想合理的使用MVC,要牢记一点建议:Rich Model is good! 这样,我们的M才是有表现力的,而对于C而言,只在里面放置应用逻辑,至于领域逻辑,全部由M实现,并在C中把控制权委派给M去实现领域逻辑。

注:此文转自baidu.com 作者:老王

Tags: , ,

发表评论