今天早上我被问了一个奇怪的问题:"你那个 PasswordLib的范例是OOP模式的,如果我想用过程式编程解决,那该怎么做?"。这个问题让我想了很久,因为这个功能需求自身就是过程化的。所 以我觉得要写一篇文章来说说OOP与过程式编程的区别了。提示:你是否使用类与对象与最后的答案实际上几乎没关系.
过程式编程
维基百科是这样定义 过程式编程的
Procedural programming can sometimes be used as a synonym for
imperative programming (specifying the steps the program must take to
reach the desired state), but can also refer (as in this article) to a
programming paradigm, derived from structured programming, based upon
the concept of the procedure call.
这显示是很正式的定义,我们来看看如何表达得更好,我认为过程式编程就是一步一步完成目标的顺序式步骤行为,每一步都可以完成某个特定的功能。来看看下面这几段代码:
很明显的过程式编码:
1 |
$m = mysqli_connect(...); |
2 |
$res = mysqli_query( $m , $query ); |
4 |
while ( $row = mysqli_fetch_assoc( $res )) { |
下面这个也是过程式编码,但是使用了对象:
2 |
$res = $m ->query( $query ); |
4 |
while ( $row = $m ->fetch_assoc( $res )) { |
这个也是过程式代码,即使用到了类:
02 |
public function getResults() { |
04 |
$res = $m ->query( $query ); |
06 |
while ( $row = $m ->fetch_assoc( $res )) { |
请注意上面的三段代码都是同样的代码结构,都是过程式模式。那么我们来看看OOP模式是如何实现同样的目标。
OOP
维基百科对OOP编程的定义如下:
Object-oriented programming (OOP) is a programming paradigm using
"objects" – data structures consisting of data fields and methods
together with their interactions – to design applications and computer
programs. Programming techniques may include features such as data
abstraction, encapsulation, messaging, modularity, polymorphism, and
inheritance.
又是一个太正式的定义。但我只认同第二部分的说明。而第
一句话说到你必须使用对象数据结构去写OOP代码实在太过牵强。你可以自己实现数据抽象,封装,消息传递,以及模块化,类集成设计而无需使用对象结构。但
我真正认为OOP之所以为OOP的真正意义是下面几个原因:第一,必须将数据抽象为模块化单元。第二,必须以多态的方式去实现。最后,也必须把功能彻底封
装。只有实现了这几点的才算真正意义的OOP编程。来看看几个典型OOP代码段:
经典OOP模式:
02 |
protected $events = array (); |
03 |
public function attach( $eventName , $callback ) { |
04 |
if (!isset( $this ->events[ $eventName ])) { |
05 |
$this ->events[ $eventName ] = array (); |
07 |
$this ->events[ $eventName ][] = $callback ; |
09 |
public function trigger( $eventName , $data = null) { |
10 |
foreach ( $this ->events[ $eventName ] as $callback ) { |
11 |
$callback ( $eventName , $data ); |
15 |
$mediator = new Mediator; |
16 |
$mediator ->attach( 'load' , function () { echo "Loading" ; }); |
17 |
$mediator ->attach( 'stop' , function () { echo "Stopping" ; }); |
18 |
$mediator ->attach( 'stop' , function () { echo "Stopped" ; }); |
19 |
$mediator ->trigger( 'load' ); |
20 |
$mediator ->trigger( 'stop' ); |
同样的模式,使用同样的方法。
02 |
function hook_register( $eventName , $callback ) { |
03 |
if (!isset( $GLOBALS [ 'hooks' ][ $eventName ])) { |
04 |
$GLOBALS [ 'hooks' ][ $eventName ] = array (); |
06 |
$GLOBALS [ 'hooks' ][ $eventName ][] = $callback ; |
08 |
function hook_trigger( $eventName , $data = null) { |
09 |
foreach ( $GLOBALS [ 'hooks' ][ $eventName ] as $callback ) { |
10 |
$callback ( $eventName , $data ); |
如你所见,这两个都是使用
Mediator Pattern
.
。都是基于面向对象的,因为它们都是被设计为 de-couple caller 模式,都提供了状态机以及模块。上面两者的区别就是一个是以传统对象实现的,而另一个全是用全局变量。我这里用专业术语"hook"来解释这个重要的原因。这也是
Drupal
使用的事件消息传递系统。
Drupal 的模块系统是将面向对象表现的非常明显的。比如它的模块系统,hook系统,表单系统等等。但是却没有使用对象。它都是在用实现的方法以及动态的消息分发。这个东西很难去权衡,所以我也并不认为是个好东西,它只是为了去证明你不需要用类或对象来实现OOP。
为什么这个很重要
很多开发者在想因为我要OOP,所以我必须使用类与对
象。还有一些开发者在想因为我要过程化编程所以我只能使用方法。这些都是不好的潜意识。OOP与过程式编程只是你去实现目的的一种方式而已,并不代表你只
有这一种方式。如果你在写代码的时候总是想着一步一步该做什么了,那你只是喜欢这种过程式编码的方式。相反如果你那时总在想着对象状态,消息传递,或者抽
象的封装。那么你就是一个典型的OOP程序员。
而类与对象只是让你更容易使用OOP而已,并不是说用了类就一定是OOP编程。
更新:OOP 数据获取
所以,有些人在问获取数据时如何使用OOP呢。来看看下面这个代码:
1 |
$mapper = new PersonDataMapper( new MySQLi(...)); |
2 |
$people = $mapper ->getAll(); |
这里的$people就是一个包含大量person的数组。注意这是抽象方式实现的,所以在你的真实项目中你并不知道你是从哪里获取数据的。这里能很容易修改你如何获取数据层的数据。
而这里发现我们需要将数据抽象封装后维持的需求是非常适合OOP模式的,所以在这里不适合过程式编程。
转至http://www.oschina.net/question/213217_61704?from=20120729