依據之前自訂的流程圖,定義系統的類別(Class)與相關的動作(方法Method)
並設計系統的操作介面(Interface)
實際玩法中,只有「莊家」和「玩家」這兩個角色,若撲克牌的處理邏輯放在這些角色裡,這樣的設計可能在 莊家 和 玩家 中都直接處理撲克牌的生成、發牌和計分等邏輯。在這個設計中,所有與撲克牌相關的處理都放在 Dealer(莊家) 和 Player(玩家) 中,這會導致代碼變得難以維護,因為每個角色都需要自己處理牌的邏輯。
以上是不好的作法,若是從原本只有「莊家」和「玩家」兩個類別擴展成三個 Class (Deck, Card, Player),可以幫助學習如何將物件導向設計中的「責任分離」概念應用到實際程式中,並理解物件導向的設計和封裝。
首先將撲克牌的數據(例如花色、點數)獨立出來,建立一個 Card 類別。這樣做可以避免在其他類別中重複使用相同的牌資料定義。Card 只需處理每張牌的屬性,像是花色和點數,這樣在「莊家」和「玩家」類別中就不再需要自行定義牌的結構。
public class Card {
public string Suit { get; set; } // 花色
public int Rank { get; set; } // 點數
public Card() {} //建立類別
}
將所有與撲克牌堆(deck)相關的邏輯(如建立、洗牌、發牌)抽離成 Deck 類別。這樣一來,「莊家」和「玩家」類別只需要從 Deck 中抽牌,無需自己處理整副牌的邏輯。
public class Deck {
private Card[] cards; //牌堆(只能透過DrawCard取得,所以設定private)
public Deck() {} //建立類別
void Shuffle() {} //洗牌(生成時,自動洗牌,所以不需要設定public)
public Card DrawCard() {} //發牌,回傳Card
}
最後,Player 類別(莊家和玩家都是Player類別)可以簡化成只需處理玩家和莊家手牌的接收和計算分數。Player 會從 Deck 類別中抽取撲克牌,並計算手牌的總分。
public class Player {
public List<Card> cards { get; set; } //手牌
public Player() {} //建立類別
public void AddCard(Card d) {} //接收Deck發過來的牌,加入手牌中
public int CalculatePoints() {} //計算手牌總分
public bool IsBusted() {} //判斷手牌是否爆牌(超過21點)
}
這樣的設計讓 Deck 處理所有與牌組相關的邏輯,Card 專門表示單張牌的資料,而 Player 只需管理手牌和分數,簡化了每個類別的責任。這樣,可以清晰地看到如何將複雜的程式分割成各自獨立的模組,並讓這些模組合作來實現整體功能,以提升代碼的可維護性和可讀性。
流程函式如下
public partial class BJ21 : System.Web.UI.Page {
protected void ResetGame() {} //初始化遊戲參數與動作(初始發牌)
protected void DrawForDealer() {} //發牌給莊家
protected void DrawForPlayer() {} //發牌給玩家
//按鈕OnClick後觸發
protected void btn_hit_Click() {} //玩家要牌,呼叫DrawForPlayer()
protected void btn_stand_Click() {} //玩家停牌,換莊家要牌DrawForDealer(),直到計分比較輸贏
protected void btn_newgame_Click() {} //新牌局(下一回合),呼叫NextRound()
protected void NextRound() {} //下一回合,呼叫ResetGame()
}
完成基本類別與方法定義後,加上邏輯與法判斷,即可完成系統開發,當然過程中還使用一些資料存取技巧,後續會補上。
若想加上其他功能,例如統計總共玩過幾次,或是玩家贏過幾次的功能,等整個基本架構完成可以運作後,再來加上即可,避免程式碼複雜化,也比較容易除錯(Debug)。