摘要:概念理解使用行為可以在不修改現(xiàn)有類的情況下,對(duì)類的功能進(jìn)行擴(kuò)充。最后將行為名稱和行為實(shí)例放到的屬性中,至此,行為的綁定就結(jié)束了。不過在解除的時(shí)候雖然都是刪掉相應(yīng)的,但是解除行為還需要解除在綁定行為的時(shí)候綁定的事件,這點(diǎn)不太一樣。
概念理解:使用行為(behavior)可以在不修改現(xiàn)有類的情況下,對(duì)類的功能進(jìn)行擴(kuò)充。 通過將行為綁定到一個(gè)類,可以使類具有行為本身所定義的屬性和方法,就好像類本來就有這些屬性和方法一樣。 而不需要寫一個(gè)新的類去繼承或包含現(xiàn)有類。在功能上類似于 Traits ,達(dá)到類似于多繼承的目的。行為的實(shí)現(xiàn)demo
"afterAttach" ]; } public function afterAttach() { echo "事件已觸發(fā)"; } } // 在控制器或者命令行下調(diào)用 $myClass = new MyClass(); $myBehavior = new BehaviorTest(); // 將行為綁定到 MyClass 的實(shí)例 $myClass->attachBehavior("test", $myBehavior); // MyClass 實(shí)例調(diào)用行為類中的屬性 echo $myClass->_val; // MyClass 實(shí)例調(diào)用行為類中的方法 $myClass->getOutput(); // MyClass 實(shí)例觸發(fā)行為類中定義的事件 $myClass->trigger(BehaviorTest::EVENT_AFTER_SAVE);行為的綁定原理
我們先來看一下 $myClass->attachBehavior("test", $myBehavior); 行為綁定的時(shí)候做了哪些事情?好的,又是我們的老朋友yiiaseComponent了
// yiiaseComponent 的部分代碼 private $_behaviors; public function behaviors() { return []; } public function attachBehavior($name, $behavior) { $this->ensureBehaviors(); return $this->attachBehaviorInternal($name, $behavior); } public function ensureBehaviors() { if ($this->_behaviors === null) { $this->_behaviors = []; foreach ($this->behaviors() as $name => $behavior) { $this->attachBehaviorInternal($name, $behavior); } } } private function attachBehaviorInternal($name, $behavior) { if (!($behavior instanceof Behavior)) { $behavior = Yii::createObject($behavior); } if (is_int($name)) { $behavior->attach($this); $this->_behaviors[] = $behavior; } else { if (isset($this->_behaviors[$name])) { $this->_behaviors[$name]->detach(); } $behavior->attach($this); $this->_behaviors[$name] = $behavior; } return $behavior; }
$myClass 調(diào)用 attachBehavior() 傳入倆個(gè)參數(shù),一個(gè)是行為名稱,另一個(gè)是行為類的名稱或?qū)嵗蛘呤菙?shù)組,接著 attachBehavior() 調(diào)用了 ensureBehaviors(),這個(gè)函數(shù)我們暫時(shí)用不到,因?yàn)槲覀儧]有在MyClass里面重載behaviors(),不過也能大概猜到ensureBehaviors()的用途了。再往下調(diào)用的是私有函數(shù)attachBehaviorInternal(),這個(gè)函數(shù)先判斷傳進(jìn)來的$behavior 是否已實(shí)例化,如果還沒有則進(jìn)行實(shí)例化,再通過$name判斷是匿名行為還是命名行為,如果是命名行為,需要查看是否已經(jīng)綁定同名的行為,如果綁定了同名的行為,會(huì)將以前的行為先解綁再調(diào)用$behavior->attach($this);[注:這里$this指的MyClass實(shí)例,也就是$myClass],這樣我們就來到了yiiaseBehavior的attach()方法,下面是attach()方法的源碼:
public function attach($owner) { $this->owner = $owner; foreach ($this->events() as $event => $handler) { $owner->on($event, is_string($handler) ? [$this, $handler] : $handler); } }
$this->owner = $owner;[注:這里$this 指的是$behavior也就是類BehaviorTest的實(shí)例],這句代碼指定了行為類的主人是誰,后面的代碼看著似乎似曾相識(shí)?是的,就是將行為類events()方法里面的事件也綁定的宿主身上,這里不具體展開,有興趣的小伙伴可以看一下淺析Yii2.0的事件Event。最后將行為名稱和行為實(shí)例放到$myClass的屬性_behavior中,至此,行為的綁定就結(jié)束了。好像也沒干什么啊,我們現(xiàn)在可以打印一下$myClass的數(shù)據(jù)結(jié)構(gòu)是怎樣的?
commoncomponentsMyClass Object ( [_events:yiiaseComponent:private] => Array ( [eventAfterAttach] => Array ( [0] => Array ( [0] => Array ( [0] => commoncomponentsBehaviorTest Object ( [_val] => 我是BehaviorTest里面的公有屬性_val [owner] => commoncomponentsMyClass Object *RECURSION* ) [1] => afterAttach ) [1] => ) ) ) [_eventWildcards:yiiaseComponent:private] => Array ( ) [_behaviors:yiiaseComponent:private] => Array ( [test] => commoncomponentsBehaviorTest Object ( [_val] => 我是BehaviorTest里面的公有屬性_val [owner] => commoncomponentsMyClass Object *RECURSION* ) ) )
可以看到$myClass已經(jīng)綁定了一個(gè)行為test,綁定了一個(gè)事件eventAfterAttach,那么綁定行為以后,是怎么調(diào)用行為類里面的屬性和方法呢?
行為的使用原理還是看一下demo里面$myClass->_val這句代碼是怎么執(zhí)行的?根據(jù)上面$myClass的數(shù)據(jù)結(jié)構(gòu)可以看出并沒有_val這個(gè)屬性,但是yiiaseComponent里面實(shí)現(xiàn)了__get()這個(gè)魔術(shù)方法,我們看一下源碼。
public function __get($name) { $getter = "get" . $name; if (method_exists($this, $getter)) { // read property, e.g. getName() return $this->$getter(); } // behavior property $this->ensureBehaviors(); foreach ($this->_behaviors as $behavior) { if ($behavior->canGetProperty($name)) { return $behavior->$name; } } if (method_exists($this, "set" . $name)) { throw new InvalidCallException("Getting write-only property: " . get_class($this) . "::" . $name); } throw new UnknownPropertyException("Getting unknown property: " . get_class($this) . "::" . $name); }
又似曾相識(shí)?是的,跟yiiaseBaseObject里面屬性的實(shí)現(xiàn)類似,有興趣的小伙伴可以看一下淺析Yii2.0的屬性Property。然后直接看注釋behavior property部分,又去調(diào)用了ensureBehaviors(),先不管,接著又去遍歷_behaviors這個(gè)屬性,根據(jù)上面$myClass的數(shù)據(jù)結(jié)構(gòu)得知,此時(shí)foreach里面的$behavior就是行為類commoncomponentsBehaviorTest實(shí)例,先通過canGetProperty判斷_val是否可讀或者存在,大家可以去看yiiaseBaseObject里面該方法的實(shí)現(xiàn)。我們這里返回的是true,然后就直接通過commoncomponentsBehaviorTest的實(shí)例$behavior返回_val的值。
根據(jù)上面獲取行為類里面屬性的流程我們注意到:
因?yàn)槭峭ㄟ^實(shí)例化行為類去調(diào)用的屬性,所以屬性是protected或者是private是獲取不到的。
如果Component綁定了多個(gè)行為,并且多個(gè)行為中有同名的屬性,那么該Component獲取的是第一個(gè)行為類里面的該屬性。
那么行為類里面的方法是怎么被調(diào)用的呢?屬性的調(diào)用是通過__get()來實(shí)現(xiàn)的,很容易聯(lián)想到方法的調(diào)用是通過__call()來實(shí)現(xiàn)的,我們查看一下yiiaseBaseObject源碼,果然里面實(shí)現(xiàn)__call()這個(gè)魔術(shù)方法,下面是源碼,然后對(duì)照上面$myClass的數(shù)據(jù)結(jié)構(gòu)一看就明白了。需要注意的是,跟上面屬性的調(diào)用一樣,方法也必須是public的,protected 、private方法是調(diào)用不了的。
public function __call($name, $params) { $this->ensureBehaviors(); foreach ($this->_behaviors as $object) { if ($object->hasMethod($name)) { return call_user_func_array([$object, $name], $params); } } throw new UnknownMethodException("Calling unknown method: " . get_class($this) . "::$name()"); }
注意到__call()里面又有一個(gè)老朋友ensureBehaviors()這個(gè)函數(shù)似乎無處不在?是的,查看一下yiiaseComponent里面的源碼我們可以發(fā)現(xiàn)所有的公有方法都調(diào)用了這個(gè)函數(shù),那么這個(gè)函數(shù)到底是干嘛的呢,其實(shí)demo里面綁定行為的方式可以稱為主動(dòng)綁定,就是我們主動(dòng)調(diào)用函數(shù)attachBehavior()去綁定行為的,對(duì)應(yīng)的就是被動(dòng)綁定了,實(shí)現(xiàn)方式就是在待綁定行為的類里面重載behaviors()這個(gè)函數(shù)就可以實(shí)現(xiàn)綁定了,相當(dāng)于一個(gè)行為的配置項(xiàng)。倆種綁定方式看個(gè)人喜好了,如果一個(gè)類需要綁定的行為很明確,推薦使用配置項(xiàng)的方法去綁定,也就是被動(dòng)綁定。下面是將demo里面的綁定方式改成被動(dòng)綁定。
new BehaviorTest() ]; } } 此時(shí)可以將demo中 $myClass->attachBehavior("test", $myBehavior); 這句代碼去掉,$myClass也是同樣可以調(diào)用類 BehaviorTest 里面的屬性和方法小結(jié)
這倆天查看了Yii2.0事件、行為的實(shí)現(xiàn)方式,覺得有很多相似的地方,都是通過yiiaseComponent來實(shí)現(xiàn)的,通過打印的數(shù)據(jù)結(jié)構(gòu)也可以看到,Component主要就是圍繞_events _eventWildcards _behaviors這三個(gè)屬性展開的,其中第二個(gè)屬性是 事件的通配符模式,也可以歸屬到 事件中,那么這樣Component的主要功能就是就是實(shí)現(xiàn)了 事件和行為。并且實(shí)現(xiàn)原理上也是相似的,都是往Component里面綁定事件和行為的handle,然后觸發(fā)事件或者行為的時(shí)候,再去回調(diào)相應(yīng)的handle。不過在解除的時(shí)候雖然都是刪掉相應(yīng)的handle,但是解除行為還需要解除在綁定行為的時(shí)候綁定的事件,這點(diǎn)不太一樣。
以上總結(jié)參考了深入理解Yii2.0,其實(shí)以前就看過,但是也只是局限于看過,沒有自己跑demo調(diào)試、查看源代碼,然后就誤以為自己明白了,其實(shí)過倆天什么都不記得了。所以現(xiàn)在通過寫博客來加深自己的理解,由于水平有限,歡迎小伙伴交流和指正。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/31590.html
摘要:概念理解第一次看深入理解的時(shí)候,我也是懵逼的,屬性不就是類的屬性嗎,有什么好說的。屬性的實(shí)現(xiàn)步驟繼承自。聲明一個(gè)用于保存該屬性的私有成員變量。如果只提供了,那么該屬性為只讀屬性,只提供了,則為只寫。 概念理解:第一次看深入理解Yii2.0的時(shí)候,我也是懵逼的,屬性不就是類的屬性嗎,有什么好說的。后來才知道Yii框架對(duì)成員變量和屬性做了區(qū)分,那類的成員變量和屬性到底是什么關(guān)系又有什么區(qū)別...
摘要:全局級(jí)別利用實(shí)例在整個(gè)應(yīng)用的生命周期中全局可訪問的特性,來實(shí)現(xiàn)這個(gè)全局事件的。類級(jí)別通過維護(hù)類的屬性數(shù)組,觸發(fā)事件時(shí)通過類名和事件名稱取到當(dāng)前類以及父類的數(shù)據(jù),再通過函數(shù)觸發(fā)。 概念理解:在某一個(gè)事件(trigger)發(fā)生的時(shí)候,觸發(fā)預(yù)先設(shè)定(on)的代碼,這是代碼解耦的一種方式。 事件按照級(jí)別分為三類 1. 實(shí)例級(jí)別 綁定事件、觸發(fā)事件的類繼承的是Component,只在當(dāng)前示例中運(yùn)...
摘要:行為所要響應(yīng)的事件重載方法,表示這個(gè)行為將對(duì)類何種事件進(jìn)行何種反饋。行為用的最多的,也是對(duì)于各種事件的響應(yīng)。當(dāng)出現(xiàn)命名沖突時(shí),行為會(huì)自行排除沖突,自動(dòng)使用先綁定的行為。目前還沒有能支持行為。 Yii基礎(chǔ) 行為(Behavior) 行為(behavior)可以在不修改現(xiàn)有類的情況下,對(duì)類的功能進(jìn)行擴(kuò)充。 通過將行為綁定到一個(gè)類,可以使類具有行為本身所定義的屬性和方法,就好像類本來就有這些...
摘要:另外,當(dāng)并行器滿足條件提前退出時(shí),所有正在執(zhí)行的子行為也應(yīng)該立即被終止,我們?cè)诤瘮?shù)中調(diào)用每個(gè)子節(jié)點(diǎn)的終止方法監(jiān)視器監(jiān)視器是并行器的應(yīng)用之一,通過在行為運(yùn)行過程中不斷檢查是否滿足某條件,如果不滿足則立刻退出。將條件放在并行器的尾部即可。 從上古卷軸中形形色色的人物,到NBA2K中揮灑汗水的球員,從使命召喚中詭計(jì)多端的敵人,到刺客信條中栩栩如生的人群。游戲AI幾乎存在于游戲中的每個(gè)角落,默...
摘要:否則會(huì)出現(xiàn)兩個(gè)下拉刷新之前之后禁用炫光和回彈效果將屬性制定為,可以禁用默認(rèn)的滾動(dòng)邊界效果。完整的地址源碼最終效果閱讀原文討論地址使用控制滾動(dòng)行為自定義下拉刷新和溢出效果如果你想?yún)⑴c討論,請(qǐng)點(diǎn)擊這里 dev-reading/fe 是一個(gè)閱讀、導(dǎo)讀、速讀的 repo,不要依賴于 dev-reading/fe 學(xué)習(xí)知識(shí)。本 repo 只是一個(gè)快速了解文章內(nèi)容的工具,并不提供全文解讀和翻譯。你...
閱讀 3869·2023-04-26 00:36
閱讀 2676·2021-11-16 11:44
閱讀 1102·2021-11-15 17:58
閱讀 1674·2021-09-30 09:47
閱讀 1216·2019-08-30 13:05
閱讀 1550·2019-08-30 12:55
閱讀 2417·2019-08-30 11:02
閱讀 2739·2019-08-29 17:01