面向对象设计和传统的过程式编程有什么区别呢?过程式编程表现为一系列命令和方法的连续调用。这种自顶向下的控制方式导致了重复和相互依赖的代码遍布于整个项目,不利于管理和维护代码。而面向对象编程则将职责从客户端代码中移到专门的对象中,尽量间减少相互依赖。
下面举个例子说明两者的区别。
假设我们要创建一个用于读写配置文件的工具。例子中忽略了具体的功能实现。
我们先按过程式方式中解决这个问题。
只需要两个函数:
function readParams( $sourceFile){
$prams = array();
// $sourceFile 中读取文本参数
return $prams;
}
function writeParams( $params, $sourceFile){
//写入文本参数到$sourceFile
}
函数定义完了,下面就是调用了:
$file = "./param.txt";
$array['key1'] = 'val1';
$array['key2'] = 'val2';
$array['key3'] = 'val3';
writeParams($array,$file);
$output = readParams($file);
print_r($output);
到此过程式开发一个读写文件的工具完成了,看起来代码写的挺紧凑的,没什么毛病。然而,现在我们被告知这个工具需要支持读写如下格式的XML文件:
<params>
<param>
<key>my keykey>
<val>my valval>
param>
params>
刚接到这个改变的时候可能会想,这个简单,在函数里加个判断不好了,如果后缀为.xml,则从xml文件中读取,如果是.txt结尾,则从文本文件中读取。你可能会这样设计:
function readParams( $source){
$params = array();
if( preg_match( "/\.xml$/i", $source)){
// 从$source中读取xml参数
} else {
// 从$source中读取文本参数
}
return $params;
}
function writeParams( $params, $source){
if( preg_match( "/\.xml$/i", $source)){
// 写入XML参数到$source
} else {
// 写入文本参数到$source
}
}
上面的两个函数中都要检测文件的扩展名,这就是面向过程不好的地方,会有很多重复的代码,Don’t Repeat Yourself是软件设计中很重要的一点。
所以我们现在用类来处理相同的问题,首先,创建一个抽象的基类来定义类型借口:
abstract class ParamHandler {
protected $source;
protected $params = array();
function __construce( $source){
$this->source = $source;
}
function addParam( $key,#val){
$this->params[$key] = $val;
}
function getAllParams(){
return $this->params;
}
static function getInstance($filename){
if( preg_match( "/\.xml$/i", $filename){
return new XmlParamHandler($filename);
}
return new TextParamHandler($filename);
}
abstract function write();
abstract function read();
}
我们定义addParam()方法来允许用户增加参数带protected属性$params,getAllParams()则用于访问该属性,获得$params的值。
我们还创建了静态的getInstance()方法来检测文件的扩展名,并根据文件扩展名返回特定的子类。最重要的是,我们定义了两个抽象方法read()和write(),确保ParamHandler类的任何子类都支持这个借口。
现在,我们定义多个子类。
class XmlParamHandler extends ParamHandler{
function write(){
// 写入XML文件
// 使用$this->params
}
function read(){
//读取XML文件内容
//并赋值给$this->params
}
}
class TextParamHander extends ParamsHander{
function write(){
//写入文本文件
//使用$this->params
}
function read(){
//读取文本文件内容
//并赋值给$this->params
}
}
这些类简单地提供了write()和read()方法的实现。每个类都将很土适当的文件格式进行读写。
客户端代码将完全自动地根据文件扩展名来写入数据到文本和XML格式的文件:
$text = ParamHander::getInstance("./params.xml");
$text->addParam("key1","val1");
$text->addParam("key2","val2");
$text->addParam("key3","val3");
$text->write(); //写入XML格式中
我们还可以从两种文件格式中读取:
$text = ParamHander::getInstance("./params.txt");
$text->read(); //从文本格式中读取
看完上面这个例子,是不是用面向对象的方式处理问题会更好呢?