unity3d筆記二:基於組件的設計


 

  在使用unity3d之前,我已經知道組件設計的概念,我們某個項目實際上也是基於組件的,雖然底層引擎只是設計了一個最簡單的組件框架,遺憾的是其他部分,並沒有按照多少組件的意思來組織代碼.這個組件失敗的地方在於,沒有提供一個很好的組件之間通信的方法.我們的組件系統使用一個interface類作為組件提供內在功能的手段.好處在於,使用該interface類你無需包含特定組件的細節(不用包含組件頭文件).壞處是,該interface類本身很龐大,因為他使用仿函數(boost function)作為與組件通信的方式,對於組件的每一個需要暴露的功能,都需要在interface中添加一個functor變量.使用仿函數的另外一個困擾是當需要將interface導入腳本時遇到困難,我相信研究一番是可以找到辦法的(最后的底線是用普通函數或類函數再把仿函數封裝一番).

  面向對象的設計模式那一套,大部分說穿了都可以轉化為某種形式的map,就是說它們其實是在表現一種映射關系,我之前在想為什么?我知道這種映射關系在oo設計中是不可避免的,但沒有一個簡單優美的解釋讓我釋懷.直到看到Go語言設計者的關於Go的舊金山大會演講,里面提到--"我已故的朋友Alain Fournier曾告訴我,他認為學術工作最基本的要求就是分工。你知道嗎?類型層次也只是一種分類法。"--是的,面向對象就是一種分類學,所以映射關系必然會被體現出來,所以map幾乎就是面向對象設計模式所必須的工具.個人覺得,使用map實現分類關系沒什么不好意思的,可是有時候非要用一層套一層的代碼來隱藏map,簡直就是居心叵測,本來沒什么意思的在我看來也變成有意思的了,你是在掩飾自己不過是使用了map的事實嗎?不過,映射關系在程序中確實重要,與使用不使用面向對象設計無關.因為程序就是對邏輯關系的抽象,邏輯關系通常又受限於現實的實體關系,而人類歸納的直覺之一既是映射.回到我們的項目,既然組件內部使用了map來查找特定的interface類,那么,何必在意再使用map來查詢內部的方法?我覺得,使用類似getCompoent("compoennt_name")->invoke("function_name")就可以了.然后減少那些枝枝葉葉的模板,設計模式方法,你就實現2個map查詢就完了,不需要更多.Go語言實際上只內建了array(數組), map(映射)兩種數據結構.array是一種內聚非常高的數據結構,map恰恰相反,是一種非常松散的數據結構.除了是一些算法的必需品外,map常被用在一些需要解除耦合的地方.設計模式的一大目標就是解耦合,map 的使用就是必然的了.但常常一些代碼中,為了解耦合,引入了過多的封裝類,這實際上與初衷背道而馳.我要學習的是,讓數據盡可能自然的表現它所代表的事物,然后呆在它顯而易見該呆的地方.

  以上是毫無頭緒的關於設計模式中map關系的吐槽.無視之.

  組件設計,打破了較為僵硬的繼承關系,而使用映射關系表示實體關系.以前在<備忘:c#接口與抽象類>中說過:

    --"而抽象類或者類的概念,是程序里具體的代碼組織關系的一種體現.關注的是同質實體之間的關系."

    --"抽象類或者說類的概念,是在某種語言環境下,對代碼間的復用和對代碼間的約束這2個耦合關系的一種實現手段."

  繼承體系遇到的困難之一就是,世間萬物的關系並不是那么單純,存在很多灰色地帶.畢竟它關注的更多是同質實體間的關系.而對於3d游戲中,一個實體,可能包含腳本,網格,物理,聲音,消息對象等等.不管你用單繼承,或是用多繼承,都會遇到繼承體系固有的問題,比如臃腫的接口,比如重載問題,比如多繼承下的菱形問題等等,這還只是一些語義問題,睜只眼閉只眼也許也就過去了,更難受的是對於一些實際操作,類似序列化到磁盤(存檔),反序列化(讀檔),繼承體系面臨的復雜性很高,例如多繼承下的反序列化工作,看一眼都要瞎掉.

  可能有人會說,組件不過就是設計模式中的組合模式.一個組件不過是一個類成員變量而已,因此還是類體系的傳統方法.組件設計某種程度上確實是一種組合模式.不同的是,傳統的類體系組合模式體現的是一種固有,內定的內聚關系.而組件模式體現的是松散的,動態的關系.比如設計一個person類,組合模式可以定義hand,foot,head,brain這4個成員變量組合為一個person,但它們缺一不可.組件系統確是person可以動態的添加hand,foot,head,brain這4個組件,也就是說完全可以有缺少brain的person出現.從這個意義上說,person已經不是一個類,而是一個集合.

  可以說,組件模式更接近於數學描述,而繼承模式,更接近於分類學闡釋.

  組件方法在面向對象語言中,也並不是完全拋開了繼承.對於基礎組件,它本身可能就是一套內聚非常高的類體系,因為它不需要動態修改,或它的專業性非常高(物理),或非常麻煩(IO),一旦完成,即作為組件只管提供功能就足夠了.組件方法本身的概念簡單明了,並沒有什么神秘的.組件設計的難點,同樣牽涉到基礎構件的實現.

  組件設計最基礎的是需要一套映射機制,體現在算法上是map,這個數據結構各個語言都有現成實現,無需困擾.而體現在語法層面的,則是需要一套反射機制.類似c#及大批動態語言,都原生的提供了這個機制.而在c++中,必須自己實現.通常,都需要實現一個完善的rtti系統.回到我們的某個項目,就是因為引擎提供了組件這個概念,也給出了查詢組件的實現,但卻沒有一個rtti系統來實現反射機制,從而實現映射關系,導致不倫不類,致使后邊的人一直不得要領.另外,我們使用的3d引擎是一款開源引擎,其他常用庫也基本是開源里拿過來的.除了邏輯層,都是外援.因此在實現構件中,你會發現材料並不是按照你組件的思路來寫的,這時候會遇上很痛苦的事.比如你希望有animaition組件,該組件應該跟entity組件划分立場清楚,但是在你使用的3d引擎中,animation中,skeleton與entity牢牢的結合在一起的,如果在entity組件里包含skeleton組件,就會有多個entity包含相同的skeleton問題,skeleton只需要一份就夠了.這是否意味着,你的entity需要分成2種組件,可animatin和不可的?或者,外層使用組件都可包含skeleton組件和entity組件,添加entity組件時將查詢skeleton.組件設計,其實也是需要整個程序代碼全面的支持的,對於大量基於外部代碼的情況,不知道有什么好的方法?組件方法也不是萬金油.代碼組織不能單純靠映射關系表現.unity中,同樣也會有類似GameObject,Collider這樣的基類出現.

   對於需要依賴很多第三方庫的程序,我覺得組件設計不是一個好的選擇.絕大多數第三方c++庫,使用的還是類繼承系統,你無法完美的與你的組件系統組合在一起,甚至就是矛盾的.抽象程度越復雜的第三方庫,越無法進行這種合作關系.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM