面向对象和面向过程编程的区别?
面向对象和面向过程编程的一个核心区别是如何分配职责。过程式编程表现为一系列命令和方法的连续调用。控制代码根据不同的条件执行不同的职责。这种自顶向下的控制方式导致了重复和相互依赖的代码遍布于整个项目。面向对象编程则将职责从客户端代码中移到专门的对象中,尽量减少相互依赖。
我们分别用面向对象和过程式代码的方式分析一个简单的例子。
假设我们要创建一个用于读写配置文件的工具,文本格式为 key:value。
先按照过程式的方式来解决这个问题。只需要两个函数。
1 <?php 2 function readParams($sourceFile) 3 { 4 $params = array(); 5 //从$sourceFile中读取文本 6 return $params; 7 } 8 9 function writeParams($params, $sourceFile) 10 { 11 //写入文本参数$sourceFile 12 } 13 14 //客户端代码 15 $file = "./param.txt"; 16 $array['key1'] = "val1"; 17 $array['key2'] = "val2"; 18 $array['key3'] = "val3"; 19 writeParams($array, $file); 20 $output = readParams($file); 21 print_r($output); 22 ?>
现在参数文件要支持xml格式。如果参数文件以.xml结尾,这时我们有两个选择,可以再控制代码中检查文件扩展名,或者在读写函数中检测。假如使用后面的方法:
1 <?php 2 function readParams($sourceFile) 3 { 4 $params = array(); 5 6 if(preg_match("/\.xml/i",$sourceFile)) 7 { 8 //从$sourceFile中读取XML参数 9 }else 10 { 11 //从$sourceFile中读取文本 12 } 13 return $params; 14 } 15 16 function writeParams($params, $sourceFile) 17 { 18 if(preg_match("/\.xml/i",$sourceFile)) 19 { 20 //写入XML参数$sourceFile 21 }else 22 { 23 //写入文本参数$sourceFile 24 } 25 } 26 ?>
如上所示,我们在两个函数中检测XML扩展名,这样重复代码会产生问题,如果我们还要求支持其他更多的格式呢。代码维护较困难。
下面我们用类来处理相同的问题。首先,创建一个抽象的基本类来定义类型接口:
1 //面向对象 2 abstract class ParamHandler 3 { 4 protected $params = array(); 5 protected $source; 6 7 function __construct($source) 8 { 9 $this->source = $source; 10 } 11 12 //添加参数 13 function addParam($key, $val) 14 { 15 $this->params[$key] = $val; 16 } 17 //访问$params属性 18 function getAllParams() 19 { 20 return $this->params; 21 } 22 23 static function getInstance($filename) 24 { 25 if(preg_match("/\.xml/i",$sourceFile)) 26 { 27 return new XmlParamHandler($filename); 28 } 29 return new TextParamHandler($filename); 30 } 31 32 abstract function write(); 33 abstract function read(); 34 } 35 36 //现在定义多个子类 37 class XmlParamHandler extends ParamHandler 38 { 39 //忽略实现细节 40 function write() 41 { 42 //写入文件 43 } 44 45 function read() 46 { 47 //读取文件 48 } 49 } 50 class TextParamHandler extends ParamHandler 51 { 52 //忽略实现细节 53 function write() 54 { 55 //写入文件 56 } 57 58 function read() 59 { 60 //读取文件 61 } 62 } 63 64 //客户端代码 65 $test = ParamHandler::getInstance("./params.xml"); 66 $test->addParam('key1','val1'); 67 $test->addParam('key2','val2'); 68 $test->addParam('key3','val3'); 69 70 $test->write(); //写入xml格式中 71 72 $test = ParamHandler::getInstance("./params.txt"); 73 $test->read(); //从文本格式中读取 74 ?>
以上两种解决方式中可以学到什么?
在过程式编程的例子中,控制代码的职责是判断文件格式,她判断了2次而不是一次。在面向对象中,只判断了一次。
过程式代码忙于处理细节,而面向对象代码只需一个接口即可工作,并且不用考虑实现细节。由于实现细节有对象负责,而不是由客户端代码负责,所以我们能够很方便的增加对新格式的支持。
我们应该如何定义类呢?最好的办法就是让一个类只有一个主要的职责,并且任务尽可能独立。