《游戲編程模式》筆記——服務(wù)定位器
意圖
提供服務(wù)的全局接入點(diǎn),避免使用者和實(shí)現(xiàn)服務(wù)的具體類(lèi)耦合。
模式
服務(wù)類(lèi)定義了一堆操作的抽象接口。具體的服務(wù)提供者來(lái)實(shí)現(xiàn)這個(gè)接口。分離的服務(wù)定位器提供了通過(guò)查詢(xún)獲取服務(wù)的方法,同時(shí)隱藏了服務(wù)提供者的具體細(xì)節(jié)和定位它的過(guò)程。
何時(shí)使用
少用。
服務(wù)定位器是更加靈活,更加可配置的單例模式。用得好可以用很小的運(yùn)行時(shí)開(kāi)銷(xiāo),換取很大的靈活性。用得不好,會(huì)帶來(lái)單例模式的所有缺點(diǎn)已經(jīng)更多的運(yùn)行時(shí)開(kāi)銷(xiāo)。
在需要功能可以全局訪(fǎng)問(wèn)時(shí),并且實(shí)現(xiàn)類(lèi)可能會(huì)運(yùn)行時(shí)切換,可以使用該模式。
設(shè)計(jì)決策
服務(wù)是何時(shí)被定位的?
外部代碼注冊(cè):
簡(jiǎn)單快捷。獲取服務(wù)的函數(shù)簡(jiǎn)單的返回指針,這通常會(huì)被編譯器內(nèi)嵌,我們幾乎沒(méi)有付出性能損失就獲得了很好的抽象層。
可以控制如何構(gòu)建提供者。
可以在游戲運(yùn)行時(shí)改變服務(wù)。
但是定位器會(huì)依賴(lài)外部代碼。任何訪(fǎng)問(wèn)服務(wù)的代碼必需假定在某處的代碼已經(jīng)注冊(cè)過(guò)服務(wù)。如果沒(méi)有做初始化,游戲可能會(huì)崩潰或者不工作。
在編譯時(shí)綁定:
快速。所有工作在編譯時(shí)完成,運(yùn)行時(shí)無(wú)需完成任何工作,是最快的方案。
保證服務(wù)是可用的。編譯時(shí)就進(jìn)行了定位,可以保存游戲完成編譯后,服務(wù)一定是可用的。
缺點(diǎn)是無(wú)法輕易改變服務(wù)。由于綁定是發(fā)生在編譯時(shí),任何時(shí)候想要改變服務(wù),都要重新編譯并重啟游戲。
運(yùn)行時(shí)設(shè)置:
使用反射在運(yùn)行時(shí)實(shí)例化對(duì)象。
可以更換服務(wù)而無(wú)需重新編譯。
非程序員也可以改變服務(wù)。
統(tǒng)一的代碼庫(kù)可以同時(shí)支持多種設(shè)置。
復(fù)雜。這個(gè)方案是重量級(jí)的,需要?jiǎng)?chuàng)建設(shè)置系統(tǒng)來(lái)實(shí)現(xiàn)外部定位服務(wù)。
加載服務(wù)需要時(shí)間,雖然緩存可以最小化消耗,但是首次使用服務(wù)時(shí)的消耗還是不可避免的。
如果服務(wù)不能被定位怎么辦?
讓使用者處理它:
讓使用者決定如何掌控失敗。如果定位器不能定義全面的政策應(yīng)對(duì)所有的情況,就將失敗傳回去,讓使用者決定什么是正確的回應(yīng)。
掛起游戲:
使用斷言?huà)炱鹩螒颉?strong>
使用者不必處理缺失的服務(wù)。
如果服務(wù)沒(méi)有找到,游戲會(huì)掛起。這會(huì)強(qiáng)迫我們解決定位服務(wù)的漏洞,但被阻塞的所有人都得等待漏洞修復(fù)。
返回空服務(wù):
使用者不必處理缺失的服務(wù)。保證了總是會(huì)返回可用的服務(wù),簡(jiǎn)化了使用服務(wù)的代碼。
如果服務(wù)不可用,游戲仍將繼續(xù)。缺點(diǎn)是較難查找缺失服務(wù)的漏洞,空服務(wù)會(huì)導(dǎo)致游戲不會(huì)像期望的那樣行動(dòng)。需要配合一些日志來(lái)查找漏洞。
服務(wù)的范圍有多大?
如果是全局可訪(fǎng)問(wèn):
鼓勵(lì)整個(gè)代碼庫(kù)使用同樣的服務(wù)。大多數(shù)服務(wù)都被設(shè)計(jì)成單一的。整個(gè)代碼庫(kù)接觸到相同的服務(wù),可以避免代碼因?yàn)椴荒塬@取“真正的”服務(wù)而到處實(shí)例化提供者。
但是失去了何時(shí)何地使用服務(wù)的控制權(quán),全局化的代價(jià)是任何東西都能接觸它。
如果接觸被限制在某個(gè)類(lèi)中:
控制了耦合,通過(guò)顯式限制服務(wù)到繼承樹(shù)的一個(gè)分支上,應(yīng)該解耦的系統(tǒng)保持了解耦。
缺點(diǎn)是可能導(dǎo)致重復(fù)的付出。如果一對(duì)無(wú)關(guān)的類(lèi)需要接觸服務(wù),每個(gè)類(lèi)都要擁有服務(wù)的引用,無(wú)論誰(shuí)定位或注冊(cè)服務(wù),都要在這些類(lèi)之間重復(fù)處理。
服務(wù)定位模式是單例的兄弟,根據(jù)需要使用更合適的一種。
Unity的GetComponent()方法中使用了這個(gè)模式,協(xié)調(diào)它的組件模式。
參考
《游戲編程模式》