收藏文章 楼主
PHP4之真OO
网友【血蜘蛛】 2005-06-21 04:00:09 分享在【时代发展的印记】版块    1    1
本文的作者Johan Persson是PHP中著名的JpGraph图表类库的开发者。本文是作者对于在PHP4中进行面向对象开发时需要注意的几个小问题的总结。

翻译: Binzy Wu [Mail: Binzy at JustDN dot COM], 水平有限, 欢迎探讨。

原文Url: aditus.nu/jpgraph/jpg_phpoo.php

简介

本文的对象是那些曾使用更加成熟的OO语言, 如Eiffel, Java, C#2 or C++(), 进行开发的朋友(如我自己).。在使用PHP4进行完全的OO开发时有着许多的语义3 (semantic) 上的陷阱4.

希本文内容可助人避我曾犯之错.

引用 VS 拷贝语义

这基本上是错误的主要来源(至少对于我来说).即使在PHP的文档中你可以读到PHP4较之引用更多使用拷贝语义(如其他我所知的面向对象语言), 但这仍将使你最后在一些细小之处困扰.

接下来的两部分用于阐述二个小的例子, 在这二个例子中拷贝语义也许会令你惊讶.要时刻牢记重要的是一个类的变量不是一个指向类的指针而是实际的类自己本身5. 大多数问题引发自对于赋值操作符(=)的误解, 即以为是给一个对象一个别名, 而实际上却是一个新的拷贝. 例如假设$myObj是某个类的实例, 并且它有一个Set()方法. 那么下面的代码也许不会像一个C++(或者Java)程序员所期望的那样工作.

代码:function SomeFunction($aObj) { $aObj->Set(10); } … SomeFunction ($myObj); …

那么现在, 很容易便会认为该函数所调用的Set()方法会作用于$myObj. 但这是错的! 其实发生的是$myObj被拷贝为一个新的, 与原对象一样的拷贝----参数$aObj. 然后当Set()方法被调用时, 它仅仅作用于本地拷贝而非原参数----$myObj. 在包含直接或间接(如上)赋值操作的地方就会发生各种各样的上述问题. 为了函数能像你所期望的那样行动(也许是), 那么你不得不通过修改方法申明来告诉PHP使用引用来传递对象, 如:

代码:Function SomeFunction(&$aObj)

如果你再一次尝试上面的代码, 那么你会发现Set()方法将作用于原来的参数上, 因为现在我们在作用中创建了一个$myObj的别名----$aObj. 但是你不得不小心, 因为即使是&操作符也不是在任何时候都能救你, 如下面的举例.

从一个引用来获得引用

假设有如下代码:

代码:$myObject = new SomeClass();$myRefToObject = &$myObject;

如果我们现在想要一个引用的拷贝(因某些理由), 那么我们要做什么呢? 你可能会由于$myRefToObject已经是引用而试图那么写:

代码:$myCopyRefToObject = $myRefToObject;

正确么? 不! PHP会创建$myRefToObject所引用对象的新拷贝. 如果你想拷贝一个对象的引用, 你不得不这么写:

代码:$myCopyRefToObject = &$myRefToObject;

在与前所述例子相当的C++的例子中, 便会创建一个引用的引用. 与其在PHP中不同. 这是一个经验丰富的C++程序员常会作的直觉假设相反的, 而这会是你的PHP程序中小BUG的来源.

请小心由此所产生的间接(传递参数)或直接的问题.

我个人所达成的结论, 即最好的避免这些语义陷阱的方法是总是用引用来传递对象或者对象赋值. 这不仅仅改进了运行速度(更少的数据拷贝), 而且可以对像我这样的老狗而言使语义更加可预测.

在构造函数中对$this使用引用

在一个对象的构造函数里初始化作为其他对象发现者(Observer6)的对象是一个常见的模式. 下面几行代码便是一个示例:

代码:class Bettery { function Bettery() {…}; function AddObserver($method, &$obj) { $this->obs[] = array($obj, &$method) } function Notify(){…} } class Display { function Display(&$batt) { $batt->AddObserver("BatteryNotify",$this); } function BatteryNotify() {…} }

但是, 这并不会正常工作, 如果你是这么实例化对象的:

代码:$myBattery = new Battery();$myDisplay = new Display($myBattery);

这么做的错误在于new时在构造函数中使用$this并不会返回同一个对象. 反而会返回最近创建对象的一个拷贝. 即在调用AddObserver()时所传送的对象于原对象不是同一个.然后当Battery类尝试通知所有它的观察者(Observer)(通过调用他们的Notify方法)时, 它并不会调用我们所创建的Display类而是$this所代表的类(即我们所创建的Display类的拷贝). 因此如果Notify()方法更新了一些实例变量, 并不像我们所设想原Display类会被更新, 因为更新的其实是个拷贝.

为了让它工作, 你必须使构造函数返回同一个对象, 正如与最初$this所象征的那样. 可以通过添加&符号于Display的构造, 如$myDisplay = & new Display($myBattery);一个直接的结果是任何Display类的Client必须了解Display的实现细节. 事实上, 这会产生一个可能引起争论的问题: 所有对象的构建必须使用额外的&符号. 就我所说的基本上是安全的, 但忽略它可能会在某些时候得到不想要的如上述示例般的作用.

在JpGraph中使用了另一种方法来解决. 即需要使用通过添加一个能安全的使用&$this引用的”Init()”方法的所谓二阶段构造来”new”一个对象(仅仅是因为在构造函数中的$this引用返回对象的一个拷贝而不如所期望的那样执行). 因此上面的例子会如下实现:

代码:$myBattery = new Battery(); $myDisplay = new Display(); $myDisplay->Init($myBattery);

如JPGraph.php中的”LinearScale”类.

使用foreach

另外一个相似代码却不同结果的问题是”foreach”结构的问题. 研究一下下面的二个循环结构的不同版本.

代码:// Version 1foreach( $this->plots as $p ){ $p->Update();}…// Version 2for( $i=0; $iplots); ++$i ) {$this->plots[$i]->Update();}

现在是一个价值10美元的问题: version1==version2么?

令人惊讶的答案是:No! 这是细小却是关键的不同. 在Version 1中, Update()方法将作用于”plots[]”数组中对象的副本. 因此数组中原来的对象并不会被更新.

在Version 2中Update()方法将如预期的作用于”plots[]”数组中的对象.

正如第一部分所陈述的, 这是PHP将对象实例作为对象本身来处理而非作为对象引用的结果.

译注:

1. OO: Object-Oriented, 面向对象.

2. 原文并无C#, 全因Binzy的个人爱好.

3. Semantic在本文中被译为”语义”, 如有任何建议请和Binzy联系.

4. C++中有一本著名的”C++ Gotchas”.

5. 这里的类应该是指Instance, 即实例.

6. 可参见”[GoF95]”, 即”Design Patterns”.

7. 有个挺有趣的关于交易的小故事:

有人用60美元买了一匹马, 又以70美元的价钱卖了出去;然后, 他又用80美元把它买回来, 最后以90美元的价钱卖出.在这桩马的交易中, 他? (A)赔了10美元; (B)收支平衡; (C)赚了10美元;(D)赚了20美元; (E)赚了30美元.

这是美国密执安大学心理学家梅尔和伯克要大学生们计算的一个简单的算术题.结果只有不到40%的大学生能够作出正确答案, 多数人认为只赚了10美元.其实, 问题的条件十分明确, 这是两次交易, 每次都赚10美元, 而很多人却错误地认为当他用80美元买回来时己经亏损了10美元. 有趣的是, 同一问题, 以另一种方式提出来:有一个人用60美元买了一匹白马, 又以70元的值卖出去;然后, 用80美元买了一匹黑马, 又以90美元的值卖出去.在这桩买卖马的交易中, 他____(把同样的五个选择罗列出来).这时, 另一组大学生在回答上述问题时, 结果大家都答对了.

在PHP5中对象模型的确是更加正确和先进了。甚至是array这样的函数在传递对象参数的时候也进行引用而非拷贝.以下php5下的Observer模式示例代码可说明. 但很多的PHP程序员并没有意识到PHP4中对象的问题.

代码:/*
*
* Binzy Wu
*
* */

class Observer
{
private $objList = array();
function __construct()
{
// nothing
}

function addObserver($subject, $method)
{
$this->objList[] = array($subject, $method);
}

public function Notify()
{
for($i=0;$iobjList);$i++)
{
$obj = $this->objList[$i][0];
$method = $this->objList[$i][1];
$obj->$method();
}
}
}
class Subject
{

public $no;

function __construct($observer, $num)
{
$this->no = $num;
$observer->addObserver($this, "test");
}

public function test()
{
print $this->no;
}
}

$observer = new Observer();
$subject = new Subject($observer, 0);

$subject->no = 2;

$observer->Notify();

?>

正如Mr.Perrson说的,本文的目的是给使用java,.net的程序员在使用PHP4时的建议。

出处:CSDN
meiguo.com 发布人签名/座右铭·有时你看似是一件很吃亏的事,往往会变成非常有得的事。
·凡事都留有余地,因为人是人,不是神,不免有错处,可以原谅人的地方,就原谅人。
·好的时候不要看得太好,坏的时候不要看的太坏。
大家都在看
回复/评论列表
默认   热门   正序   倒序
meiguo.com 创始人

emotion

1   2005-06-21 04:00:09  回复

回复/评论:PHP4之真OO

暂无用户组 升级
退出
等级:0级
美果:
美过
精华推荐
  1. 完整曝光:美国前总统【川普(特朗普)】遭遇刺客的前前后后
  2. 漂亮国再次遣返中国移民,这批有131人!
  3. 《潜望》对话李开复:如果美国形成AGI霸权,中国咋办?
  4. 关于EVUS的填写心得和常见问题
  5. “极右翼”控制移民政策!特朗普政府的内阁名单曝光
  6. 悲惨回顾:美国历史上的十大枪击案盘点
  7. 中国防长:“谁胆敢把台湾从中国分裂出去,必将粉身碎骨、自取灭亡”
  8. 关于美国大学的学费开支
  9. 移民故事:入赘美国的河南保安【蔡小华】现状
  10. 坐火车“游览全美国”的14条线路盘点
  11. 美国房市降温?待售房屋开始下调要价!
  12. 《黑神话:悟空》发行仅3小时后竟然就这样了!
  13. 珠海航展:轰20的先行版遭遇美国酸溜溜了
  14. 巨型公司:市值已超3.5万亿美元,约合18个阿里巴巴!
  15. 新罕布什尔州的一位女子在领取彩金的现场捐出5000万美元
  16. 加州公司的市值盘点 top10
  17. 在加州海滩捡蛤蜊,72个罚9万美元!
  18. 人身安全“没保障”的原因?赴美留学的趋势遇冷
  19. 拆解:太精致啦!到底是苹果M4 Mac mini牛?还是华强北更牛?
  20. 号外:伊隆·马斯克的第11个孩子出生了
  21. 美国人口流动数据剖析:年轻富有群体搬家去哪儿了?
  22. “走线”路不通了?拜登政府颁布最严边境令?
  23. 中国人即将登月!
  24. 福建人在纽约:有多少人通过走线(偷渡)到纽约的?
  25. 伊隆·马斯克在“We, Robot”三连发:Cybercab、Robovan及Optimus!
  26. 关于美国的社保(全面解读)
  27. 碧昂斯和巨石强森这样的美国巨星在大选中,如何站队的?
  28. 宁愿混居美国,华人姑娘袒露了不愿回国的真相!
  29. 回归之王:唐纳德·特朗普“赢得又大又快”
  30. 佛罗里达遭遇的飓风可以影响到美国大选结果?
  31. 深入剖析:性在人类交往中的作用
  32. 能决定2024选举结果?特朗普即将放大招了!
  33. 五星红旗在月球背面升起!阿波罗登月遭遇再度质疑?
  34. 苹果公司在2024秋季的新品发布会(懒人速览)
  35. 从旧金山到洛杉矶,美国西部旅行的完整实录
  36. 世上只有男人和女人!~ 特朗普总统:上帝只创造了两种性别,无其它!

美国动态 美果转盘 美果商店

Your IP: 18.116.85.204, 2024-11-26 19:47:54

Processed in 0.45123 second(s)

头像

用户名:

粉丝数:

签名:

资料 关注 好友 消息
已有0次打赏
(1) 分享
分享
取消