作者gpmm (银色)
看板PHP
标题[心得] 浅聊物件导向在 PHP 中的观念和应用 # 2
时间Sat Jul 30 21:15:38 2011
聊过了类别的封装,我们要继续实例讨论物件导向的第二特性,
也就是所谓扩充的能力。
这有点像是在玩游戏,
假设你在游戏里操作着一只小猪,这只小猪有一个特技叫做「吃」,
每次当你遇到某个谜题时,都必需让小猪「吃」的能力成长,
可能需要吃铁,吃火,吃土…等等,
而每当你拿到一种新的「吃」的能力,小猪的封号也会随之改变。
上面这个情景很类似我们对於物件导向扩充的描述,
让我们继续延伸前一篇的程式内容,把这个游戏情景给套用进去
目前我们手上持有的是一个叫 dataHandler 的类别,
里面一共具有三种不同的输出方式,分别是
output_select
output_list
output_radio
仔细想想,每次都我们输入资料产生出一个物件时,
实际上真正会使用到的输出行为都只是
三种中的其中一种,
那换个角度,我们是否能让这个处理资料的类别具有合适的扩充能力?
ok,没有命题就不会有 fu,我们需要一个假设性的命题,
如果当你在执行这个案子的时候,你敏锐的触感神经已经预计到,
客户很有可能又额外提出六种输出方式和三种统一验证,
那这个时候要做什麽就很明确了,
我们需要这个类别具有
输出上的可扩充性,套用故事里的情境,
也就是这个小猪(类别),需要可以增加不同的吃(输出)的能力。
当然最简单的方法就是增加类别内的 output_* 函式,
反正客户要几个就加给他几个,
但也可以这样思考,我们有没有可能把整个扩充方式从扁平改成立体?
既然现在面对的扩充是针对「输出」这件事,
那就来规规矩矩把输出部份给抽象化,
於是原本的类别会变成:
class dataHandler {
var $_data = Array ();
function __construct ($data) { (省略) }
function data_remove_sexy () { (省略) }
function data_check_add_admin () { (省略) }
function output () {
// 空的
}
}
仔细看,三种不同的 output 此刻只剩下一种,
而且这个 output 函式还是个货真价实的空荡荡的函式,里面没有半行程式码,
为什麽呢?
因为我们要从原本「在类别内的横式扩充」,
改成「藉由类别衍生所产生的直式扩充」,
刚刚上面这个 class dataHandler 其实只是一个初始类别,
真正可以拿来使用的会是下面这三个子类别:
class dataHandler_OutputSelect extends dataHandler {
function output () {
// 输出 select 的程式码
}
}
class dataHandler_OutputList extends dataHandler {
function output () {
// 输出 list 的程式码
}
}
class dataHandler_OutputRadio extends dataHandler {
function output () {
// 输出 radio 的程式码
}
}
如果你需要进行一个 select 输出,你会这样使用:
$data = mysql_fetch_* ($sql);
$dh = new dataHandler_OutputSelect ($data);
$dh->output ();
如果今天是一个 radio 输出,你会这样使用
$data = mysql_fetch_* ($sql);
$dh = new dataHandler_OutputRadio ($data);
$dh->output ();
我知道板友们一定会开始有种被骗的感觉,这跟
$dh->output_select () 和 $dh->output_radio ()
的作法有什麽不同?而且感觉还多写了一堆 code(额外三个 class)!
请容小弟解释一下,这两种不同作法当中存在着很巨大的差异,
其一是这样的程式在扩充上会更清楚明了,
因为往往一个类别不会只有单个函式需要做扩充,
如果同一个类别内存在着一堆
相似又不同的函式,那真的是阅读和维护上的痛苦。
其二是这样的作法让物件本身操作具有了
一致性,
只要是 dataHandler 的子类别,
你永远知道它具有一个关键的函式是 output,而且可以毫不犹豫地呼叫它,
这点非常重要,但我们会在下一篇里面再来详谈,
因为也就是垂直扩充的物件具有了操作的一致化,第三个物件导向特性才得以成立
其三,则是一个比较难以理解的概念,那就是「决策点」的不同,
(这部份如果看不懂也没有太大关系,因为需要有经验才比较能体会)
如果要打个比方,「决策」的概念比较像是篮球赛里面,
你有整整 30 秒可以出手,跟被强迫在最後 0 秒出手的不同是一样的,
这样讲好了,
当我们在尝试把一整个程式逻辑慢慢拆解成几种类别时,
有的类别存在周期会很长,有的则是很短,
也就是说,
有些类别会在你程式一开始的时候就已经产生物件,并持续使用到最後,
(最常见的就是封装成类别的资料库操作)
有的类别则是「当下产出物件」-「当下使用」而已,
(就像是我们刚刚的资料输出类别)
所以此时你在设计程式流程时会有两个选择,
从类别 new 出物件时做决策,
(像决定你要使用哪一种输出
类别,
dataHandler_OutputSelect 还是 dataHandler_OutputRadio)
或是在最後函式呼叫时做决策,
(像决定你要使用哪一种输出
函式,
dataHandler 下的 $dh->output_select 还是 $dh->output_radio)
这当中各有各的使用时机,就端看你整体程式的运作流程要怎麽设计来决定,
其中很多复杂的运用同样也是在导入了设计模式之後才会发现的。
讲到这里,物件导向的第二特性 - 继承也差不多讲完了。
关於继承的详细定义和宣告方式,不熟悉的板友可以再去看一下,
这部份小弟就不赘言了。
--
头痛中…(炸)
--
※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 114.45.241.224
1F:推 chenstin:推! 07/30 22:01
2F:推 weiyucsie:推 07/30 22:44
3F:推 roga:推 :P 07/30 22:57
4F:推 bobju:推! 07/31 10:12
5F:推 hertz:NICE 07/31 18:40
6F:推 qmo668:推! 10/02 01:47