|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
终于理解了数据库的概念,而且让你兴奋不已的是你终于可以通过PHP来连接数据库了,这期间你是怎么学会的,我们不去考证了,但是事实证明,你已经可以了。 本文的作者Johan Persson是PHP中有名的JpGraph图表类库的开辟者. 本文是作者关于在PHP4中停止面向对象开辟时需求注重的几个小成绩的总结.
翻译: Binzy Wu [Mail: Binzy at JustDN dot COM], 程度无限, 接待切磋. 2004-2-4
简介
本文的对象是那些曾利用加倍成熟的OO [1] 言语, 如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利用援用
在一个对象的机关函数里初始化作为其他对象发明者(Observer[6])的对象是一个罕见的形式. 上面几行代码即是一个示例:
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 1
foreach( $this->plots as $p )
{
$p->Update();
}
…
// Version 2
for( $i=0; $i<count($this->plots); ++$i )
{
$this->plots[$i]->Update();
}
如今是一个价值10美元的成绩[7]: 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)出入均衡; |
|