虽然现在都是使用面向对象,但你真的使用面向对象了吗?还是你一直在使用过程式编程
区别
面向对象和过程式编程的一个核心区别是:如何分配职责。过程式编程表现为一系列的命令和方法的连续调用。控制代码根据不同的条件执行不同的职责。面向对象编程将职责从客户端代码移到专门的对象中,尽量减少相互依赖。我们举个例子说明:
面向过程式的编程:
我们先用下面的格式来读写文本,readParam读取文件内容,然后存到关联数组里,最后返回。writeParam 函数会循环遍历关联数组,然后将键值对写入文件。
function readParam($sourceFile){
$param = [];
//从$sourceFile读取文件内容
return $param;
}
function writeParam($param, $sourceFile){
//写到$sourceFile文件里
}
上面这个工具好理解,也易于维护,但现在需求改变,需要支持读写xml和text文件。我们进一步改变代码,如下所示:
class file{
function readParam($source){
$param = [];
if (preg_match("/\.xml$/i", $source)) {
//从xml文件中读;
} else {
//从text文件中读
}
return $param;
}
function writeParam($param, $source)
{
if (preg_match("/\.xml$/i", $source)) {
//写xml参数到文件中;
} else {
//写text参数到文件中
}
}
}
面向对象的编程:
abstract class ParamHandle
{
protected $params = [];
protected $source;
public function __construct($source)
{
$this->source = $source;
}
public function addParam($key, $value)
{
$this->params[$key] = $value;
}
public function getAllParams()
{
return $this->params;
}
/**
* 获得实例
* @param $filename
* @return object
* */
static function getInstance($filename)
{
if (preg_match("/\.xml$/i", $filename)) {
return new XmlHandle($filename);
} else {
return new TextHandle($filename);
}
}
abstract function read();
abstract function write();
}
class XmlHandle extends ParamHandle
{
public function read()
{
echo 'this is xml file read' . PHP_EOL;
// TODO: Implement read() method.
//读取xml文件内容,并赋值给$this->params
$this->addParam('a', 'aa');
$this->addParam('b', 'bb');
$this->addParam('c', 'cc');
return $this->getAllParams();
}
public function write()
{
echo 'this is xml file write' . PHP_EOL;
// TODO: Implement write() method.
//写入xml文件,使用$this->params
$data = $this->params;
echo '<pre>';
print_r($data);
exit;
}
}
class TextHandle extends ParamHandle
{
public function read()
{
echo 'this is text file read' . PHP_EOL;
// TODO: Implement read() method.
//读取xml文件内容,并赋值给$this->params
$this->addParam('a', 'aa');
$this->addParam('b', 'bb');
$this->addParam('c', 'cc');
return $this->getAllParams();
}
public function write()
{
echo 'this is text file write' . PHP_EOL;
// TODO: Implement write() method.
//写入xml文件,使用$this->params
$data = $this->params;
echo '<pre>';
print_r($data);
exit;
}
}
$source = './param.xml';
$type = (int)$_GET['type'];
if ($type) {
$instance = ParamHandle::getInstance($source);
$instance->addParam('a', 'aa');
$instance->addParam('b', 'bb');
$instance->addParam('c', 'cc');
$instance->addParam('d', 'dd');
$instance->write();
} else {
$instance = ParamHandle::getInstance($source);
$data = $instance->read();
echo '<pre>';
print_r($data);
exit;
}
echo 'ok';
说明:
- 职责
- 过程式编程: 条件语句被绑定到函数,判断流程被隐藏起来;忙于处理细节
- 面向对象: 静态方法getInstance()进行格式化选择;只需要一个接口即可工作
- 内聚:是一个模块内部各成分之间关联程度的度量。理想情况下,应该各组件职责清晰、分工明确。如果代码间的关联范围太广,维护就会很困难---因为修改某部分代码的同时需要修改相关的代码
- 前面ParamHandle类将相关的处理集中起来,用于处理XML类的方法间可以共享数据,并且一个类方法中的改变可以很容易反映到另一个方法中,可以说ParamHandle是高度内聚的。过程式的例子,把相关的过程分离开,导致处理XML的代码在多个函数中同时出现
- 耦合:当系统各个部分代码紧密绑在一起时就会产生紧密耦合,这时一个组件的改变会迫使其他部件随之改变。过程式代码容易产生耦合问题。例如在过程示例中,writeParam和readParam中,使用了相同文件扩展名测试来决定如何处理数据。因此要改写一个函数就不得不同时改写另一个函数。例如增加一种新的文件格式,就要在两个函数中按相同的方式都加上相应的扩展名检查代码,这样两个函数才能保持一致。面向对象的示例则将每个子类彼此分开,也将其与客户端分开。如果需要新增参数格式。只需要简单地创建相应的子类,并在父类中的静态方法getInstance()中增加一行文件检测代码即可。
- 正交:指将职责相关的组件紧紧组合在一起,与系统环境隔开,保持独立。正交主张重用组件,期望不需要任何特殊配置就能把一个组件插入到新系统中,这样的组件有明确的与环境无关的输入和输出。正交代码使修改变简单,因为修改一个只会影响到被改动的组件本身。正交代码更加安全。bug的影响只局限于他的作用域之中。内部高度相互依赖的代码发生错误时,很容易在系统中引起连锁反应。