看板 C_and_CPP 關於我們 聯絡資訊
※ 引述《newdominic (Daily Game)》之銘言: : 推 QQ29:光t大說的大方向 我就很難懂了....context class是什麼意思阿 06/22 23:44 http://tinyurl.com/242fb7b context class 就是把責任委派給 state 繼承體系的那個 class。 學 design pattern 有一個目的是為了溝通方便, 所以 pattern 內的組成物其實都有一個通用的概念名稱。 實際上寫 code 不一定要照著命名, 但討論上就是直接用它們快速定位一個 class 扮演什麼角色。 : 感謝t大的回覆 : 但我也不是很瞭解t大的意思||| : 抱歉,我似乎把問題簡化過頭了 : 我目前在做一個牌類遊戲 : 遊戲的主體是一個牌桌類別 (class Table) : 主要運作的函式為 Update() 與 Render() : 遊戲的過程分成七種狀態 : 最初的寫法是用 switch 判斷狀態,再去呼叫對應的函式 : 但函式一多以後 Table 類別就變得難以整理 : 所以想嘗試把每個狀態寫成一個類別 : 這樣每次進入 Table::Update() 和 Table::Render() 時 : 就呼叫目前狀態的 Update() 與 Render() : 於是在將狀態獨立成單一類別的時候 : 我把該狀態原先在 Table 裡面要操作的 data members 移到狀態類別裡 : 但在這七個狀態中,又有有 3 、 2 、 2 三組子狀態的 Render() 是做一樣的事情 : 所以我就把重複的部分再向上提,形成如下的繼承樹 : (L1) (L2) (L3) : ┌StateA1 : ┌StateA┼StateA2 : │ └StateA3 : │ : │ ┌StateB1 : State┼StateB┴StateB2 : │ : │ ┌StateC1 : └StateC┴StateC2 : Update() 時使用 L3 類別提供的定義 : Render() 時使用 L2 類別提供的定義 : 這樣在 L2 的類別成員用 static 宣告可行 : 但滿滿的 static 變數讓我有點怕(心理作用?) : 所以想知道是否有除此之外的解法 其實正常的 state 實作繼承體系只會有一層, 會有兩層可能要看你當初的狀態機圖有沒有問題, 再來就是那些重複的 code 是不是本來應該是 context class 負責做的事。 區分是不是 context class 該做的事, 可以看看重複的 method 內容是不是在處理一份共同的資料, 如果是的話通常應該把它放回 context class 去。 當然並不是所有共通的資料都要這樣做, 重點在於這些共通的資料在外界存取 context 物件時有沒有影響性, 如果沒有的話你當然可以留在 state 裡。 雖然說 context 只是負責接收外面的 requests, 不需要知道如何 handle 那些收到的 requests, 而是將 handle 這些 requests 的責任委派給 states。 但它本身還是需要知道如何處理內部資料和儲存資料, 這樣才有辦法跟外界進行更進一步的溝通。 當然如果你的 context 是 MVC pattern 的下的 controller 時, 處理和儲存資料的責任又應該再委派給 model, 這個又是另一個故事所以不深入講下去, 其實後面提到的「專門負責存資料的物件」跟這概念會有些相似。 所謂知道如何處理資料, 就是提供比 setter/getter 還高階一點的 method, 被呼叫一次就能 set 很多東西。 初期你可能識別不出這樣的 method, 所以可以直接提供 setter/getter 沒差, 日後再整理起來就可以了。 這些 setter/getter 也未必要是 public, 你可以把 abstract state 設成 friend, 然後讓 concrete states 呼叫它們來做 set/get, 這樣就能避免把所有 concrete states 都列進 friend。 當然如果你討厭用 friend 或怕弄亂 context 的對外介面, 可以再造一個 class 專門放這些資料, 然後初期一樣做一堆 getter/setter, context 呼叫 states 裡的 method 時就把放資料的物件整個丟過去。 如果你想連參數列都省下來... 一般 context 物件的 constructor 其實都會負責建構所有 states, 這樣這些 state 物件就是專屬於這個 context 物件所用, 你可以在這個時間點就事先把 pointer 或 reference 存在每個 states 的實體內。 上面講的都是能接受複數 context 實體時的做法, 當你的 context 只有一個實體時確實可以省點麻煩。 只是將來接你 code 擴充的人在想要有多份 Table 實體的話, 你可能會三不五時覺得背後好像有點涼涼的。 至於你的 static 解法, 從你的需求上來看似乎整個程式只會有一個 Table, 那麼你可以把 Table 和所有的 concrete states 都做成 singleton。 singleton 本來就常伴隨在這邊出現, 所以也沒有什麼濫用不濫用的問題。 最重要的是如果你的 Table 不是 singleton, 這樣只要同時有兩個 Table 實體在跑, 你 state 內的 data 就完全亂了。 回到現實面。 如果你真的有重複的實作必須把 state 家族做成階層式, 其實這也沒差, 你可以另外造一個 singleton 物件專門放共通資料。 這並不算濫用 singleton, 所以你第一篇擔心的如果是這件事, 那就放心去寫。 當然這招必須在你的 context/states 都是 singleton 的前提下才行, 如果你的 context 允許多重實體那就是在 constructor 內指定, 就是用前面講的存 pointer/reference 的方法。 最後我也很懷疑你的 state 是不是真的只有 update() 和 render(), 也許你應該框更大一點的東西進來, 又或者你應該把這兩個 method 切細。 因為 state 的 public method 被呼叫後通常會觸發狀態轉移, 從製作 state machine diagram 的角度看過來的話, 我覺得觸發狀態轉移的時機有點怪怪的。 這是我覺得也許應該框更大一點的東西進來的原因。 並不是 context 寫了 update() 跟 render(), 你的 state 就一定只能寫這兩個 methods 或是一定要有這兩個 methods, 它們可以是更細小一點的東西, 這就是我說的也許應該切細的原因。 到底是哪種就真的要深入去看需求規格和架構了, 我最近比較沒這個時間所以還是留給你自己判斷和決定。 : ===== : 另外,t大在推文中提到的 context class ,不曉得是不是指文中的 Table 類別? 是。 : 我有想過在 L1 的 State 類別宣告一個 Table * : 在 Table 建構 L3 的狀態物件時,把 this 做為建構參數傳給它 這是其中一種標準做法, 又或者可以造一個 TableData 或 TableModel 之類的來傳。 : 這樣全部的狀態類別就不需要擁有 data members : 但在用 Table * 操作的時候會呼叫大量的 setter/getter,讓我難以取捨 : 這部分不知道我有沒有會錯意? setter/getter 最後是有機會可以消除、合併和化簡的。 如果你是造一個 TableData 來傳, 裡面又只有 setter/getter, 那就符合了 Martin Fowler 列的 bad smells 裡的 data class。 大量呼叫 setter/getter 的程式碼終究有機會發生重複, 而那些重複的 code 就能從呼叫點移進 data class, 然後這個 data class 就會開始進化。 所以這雖然算 bad smells 但不代表你初期不能這樣設計, 反倒是在初期更應該這樣寫, 寫得越糟糕越好, 這樣才能讓你將來記得回來修改這邊。 這種做法有點類似 C++ 的爸爸故意把 cast 搞得很長一串那樣, 就是故意搞得很醜引人注意, 當然他這樣做的目的跟這不太一樣就是了。 : 小弟入門 OO 的時間很短 : 不曉得哪種寫法從 OO 的角度來看是比較好的,或是有其他更好的方法? 一個大原則就是... 用 OO 就不要怕打很多字。 因為 OO 被人過度強調 code 的 reuse 能力, 所以讓很多人認為需要打很多字的就偏離了 OO。 但是其實用 OO 真的要打很多很多字。 -- Ling-hua Tseng (uranus@tinlans.org) Department of Computer Science, National Tsing-Hua University Interesting: C++, Compiler, PL/PD, OS, VM, Large-scale software design Researching: Software pipelining for VLIW architectures Homepage: http://www.tinlans.org -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 118.160.105.144 ※ 編輯: tinlans 來自: 118.160.105.144 (06/23 12:51)
newdominic:學到很多東西,感謝t大! 06/23 14:12
newdominic:這樣看來我應該重新設計一次架構會比較好 06/23 14:14
newdominic:希望以後還有機會聽到t大講故事 XD 06/23 14:15
loveme00835:真的要很多很多字~ 06/23 14:53
snowpoint:推背後會涼涼的XD 06/23 16:57