JAVA面向物件設計例項之一發牌比較大小
程式功能闡述如下:
由電腦隨機從撲克牌堆中抽取前兩張牌,一張屬於使用者,一張屬於電腦,比較其大小:
大小規則:數字規則:
2<3<4<5<6<7<8<9<10<J<Q<K<小王<大王
花色規則:
方塊<梅花<紅桃<黑桃
比較規則:數字優先,同數字,花色優先。
專案截圖:
針對這個專案,我們先把思路做一下整理:
在這個專案中用到了資料,我們先從資料入手,進行資料的分析:
對於發牌比大小,資料就是牌。
單純說牌,還有一個前提是比較大小.那麼在資料進行設計的時候,比較簡單和直接的設計就是:
按照牌大小順序排好。
我們先看我們定的規則:
1、黑桃>紅桃>梅花>方塊
2、無論任何花色2<3<4<5<6<7<8<9<10<J<Q<K<A
3、大王最大,其次是小王
我們在排這個列表的時候可以根據方塊2、梅花2、紅桃2、黑桃2、方塊3、….黑桃A 、小王、大王這個順序來組織和排序資料。
排完以後,諸如:方塊2、梅花2、紅桃2 、黑桃2等內容都可以是一個String型別,根據String型別的特點:一個String就是一個char陣列,在堆記憶體裡有一定的空間儲存。54張牌就要有54個堆記憶體空間來儲存。在對這個堆記憶體空間操作的過程中,也對字串進行處理,而String的處理其實就是在String會進行陣列複製,進而浪費記憶體。
針對這一問題,我們可以採用技術:讀寫分離。
把字串做為一個變數也好,靜態也罷,只是儲存著牌的資訊,也不能比較大小,真正比較大小還需要我們的整型資料,我們能不能把牌的資訊和整型資料結合起來,一一對應,一個整型資料對應一個牌的資訊,當我們使用這個牌的時候,再進行對應關係的對映,我們的操作只針對這個整型的一個集合。這就構成了我們的讀寫分離,讀的是牌的資訊和整型數的對應關係,寫或者說操作的都是整型的列表或集合。
那麼整型庫的型別可以定義成ArrayList<Integer>
整型庫與牌資訊的對應關係我們應用Map集合:
Map<Integer,String>
一副牌理好以後,我們需要洗牌,也就是打亂牌。
打亂牌始終對ArrayList中的Integer操作,取得這張牌也可以對ArrayList的Integer操作,比較也是對ArrayList的Integer操作。這一期間都是對Integer操作,直到需要對應關係的時候我們直接找那個key對應的String對映。
這樣資料的思路出來了:
也就是我們可以定義一個數據類,比如這個類叫PuKe。這裡至少先有兩個成員變數:
一個list列表,裡面存0-----53的整數。
一個Map 集合裡面存0—53和方塊2----大王的對應關係
初始化的時候可以對這兩個成員變數進行賦值:
就是把花色牌和比較的數字一一對應。
這樣我們得到的程式碼可以是:
先定義兩個成員變數:
再進行初始化:
初始化以後進行賦值:
思路:我們可以把花色和點數進行迴圈匹配出牌面來,用一個標誌整數位的值來進行累加計算整型值。
這樣把資料放在列表中就變成以下形式:
這個資料裡缺少大王和小王,我們再把大王和小王對應的數值也放進去。
在資料PuKe類的建構函式裡有了以下程式碼以後,我們可以用map的keySet遍歷方法:
可以在主類中呼叫一下,看一下結果是不是我們想象中的樣子:
放在Map集合中的結果已經完成,也是我們期望的順序。下面我們要進行的就是集合中的資料打散,進行洗牌的操作。
洗牌實際上就是把紙牌集合隨機進行兩兩交換,進行若干次,就可以把集合中的元素順序弄亂。由於我們採用讀寫分離,實際上我們打亂的是Integer整型資料的集合。
實際的程式碼實現如下:
當然,由於collections工具類的引用,這段程式也可以用工具類實現:
把集合打散後,相當於我們已經完成了撲克牌集合的準備。下一步我們要做的事就是能夠從牌頂拿出一張牌給玩家,再拿出一張牌給電腦,然後比較大小。
從牌庫頂拿一張牌我們需要寫方法get
但要注意的是,我們每次拿的牌是不一樣的,才能保證電腦和玩家的牌不同,我們就得需要定義一個index變數,這個變數存放的就是牌庫的索引值,每次拿牌索引值增加,就可以保證每次拿到不同的牌,不過前提是這個值不會因為程式的初始化而變化,所以用靜態變數來定義這個index值。
這樣程式碼變成:
這樣我們就實現了一個拿牌的過程。
不過我們拿到的是數字,在外面呼叫後,一定會進行數字對應的牌面的轉換,我們就需要把map這個存放牌值的集合進行封裝後的getter,setter方法,供外面呼叫後把數字轉成相對應的牌面。
整個資料類我們已經完成了,下面我們就要進行遊戲角色類的設定了。
對於遊戲角色類,我們不難分析,由電腦和使用者端同時拿一張牌,然後比較大小,從這個要求中,就會明白,我們需要電腦類、使用者類,由於這兩個類都需要從資料牌庫類中取牌,方法也是一樣的,所以我們可以找到他們的共同父類,我們暫且叫選手類。
在選手類中我們不難分析,他們應該有成員變數就是他們各自的名字,我們可以簡單處理,初始化的時候就把名字進行賦值,然後有一個方法從資料牌庫中拿牌,拿到牌後轉化成我們需要的牌面資訊。
以上是選手類的相關程式碼:
電腦類和使用者類直接繼承於選手類即可,不用做方法的重寫,但從繼承的相關理論中我們知道,構造器不能繼承過來,我們在子類繼承中把構造器用super關鍵字來繼承過來,這樣子類就有自己的名字了,再進行比較牌面。
於是電腦類Computer就為:
使用者類就成為了:
參與的角度類建立成功後,我們建立操作類,就是遊戲的整個執行過程類:
一般的遊戲過程類都包括:遊戲初始化方法,遊戲執行方法,遊戲判斷方法和遊戲結束方法,
於是這個執行過程類就包括:
Init() 方法 game()方法 judge()方法 gameover()方法
而對於這個遊戲,init()可以實現發牌,發完牌直接比較,judge()方法,最後再gameover方法,不需要game()方法
我們可以寫一個執行過程類的介面,讓開發者都遵循我們統一的命名標準和規範。
接下來我們定義一個操作類實現這個介面
在這個操作類中,我們只需要實現init()、judge()和gameover方法即可。
Init()方法中例項牌庫、電腦、使用者,然後電腦和使用者從牌庫中取牌。
這裡定義成員變數的Computer、Player、Puke 三個變數的原因是由於這三個成員變數在init()方法、judge()方法和gameover方法中都要呼叫,所以定義一個全域性的成員變數。
new Computer和new Player的過程中直接給這三個成員變數的name賦值,即參與遊戲的兩個角色,然後new PuKe()是把撲克牌產生,並呼叫PuKe中的shuffle方法打亂。再由Computer成員變數comp呼叫其父類的方法fetch一張牌,再由Player成員變數player呼叫其父類的方法fetch一張牌,然後執行judge();
這是上面程式碼的相關邏輯。
然後我們看judge()的完成。
在judge方法中,我們明顯需要得到一個量,這個量就是撲克牌對應的整型值,而這個值我們也放在了撲克牌類中的ArrayList列表中,我們即然要取這個值,就需要知道電腦類、玩家類中fetch方法中獲取的牌對應的整型值是多少,我們沒有定義這個獲取方法,因此需要把電腦類和玩家類的父類再增加一個成員變數,這個變數就是key,我們通過fetch()方法獲取了牌之後,還要把key值儲存一下,然後定義getter方法讓外界可以呼叫,這樣judge就可以進行key值的比較了,繼而來決定牌的大小。
先看改寫的選手類:
增加key值:
定義getter方法,外界不需要對其設定,所以不用設定setter。
在fetch的時候我們把key 值也儲存下來。
這樣選手類的key值設定完成後,電腦類和使用者類的key值也設定了。
下面我們完成操作類的judge()方法,程式碼如下:
看邏輯也非常簡單。
對比電腦的key值與玩家的key 值,大了話“玩家輸了”,小的話“玩家贏了”。
最後呼叫gameover()方法.
gameover()方法也非常簡單,就是輸出一句“遊戲結束,歡迎使用“。
這個遊戲程式的最後,我們需要定義主類來呼叫,我們叫MainLei,這個名稱可以自己定義。在其中只要初始化一下游戲操作類,即CaoZuo類例項化和呼叫其init方法即可:
至此,整個程式完成。我們可以看一下整體的程式目錄結構,養成良好的結構習慣。