首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

劣質代碼評析:《寫給大家看的C語言書》21點遊戲重構實戰(六)

     編輯:關於C語言

經典教材代碼的現代化重構之路

《寫給大家看的C語言書》附錄B的21點(Blackjack)遊戲雖然完整實現了遊戲邏輯,但代碼結構混亂、全局變量濫用、函數耦合度高,是典型的「能跑不能維護」代碼。本系列文章將逐步重構該程序,展示從面向過程到模塊化設計的演進過程。

原始代碼主要問題

  • 單文件巨石結構:所有邏輯塞在main.c,無模塊劃分
  • 全局變量污染:cards[]、playerPoints[]等十餘個全局變量
  • 函數職責不清:dispCard同時負責顯示邏輯與分數計算
  • 魔術數字遍地:DEALER 0、ACEHIGH 1等宏定義缺乏語義
  • 控制流混亂:巢狀if-else超過5層,極難追蹤

重構第一步:模塊劃分與數據封裝

將單體拆分為四個核心模塊:

/* poker.h - 撲克牌管理 */
typedef struct {
    int cards[52];
    int num_cards;
} Poker;

void poker_init(Poker *p);
int poker_draw(Poker *p);  // 抽牌並自動洗牌

/* gamer.h - 玩家狀態 */
typedef struct {
    int score;   // 最終分數
    int low;     // A按1計算的分數
    int high;    // A按11計算的分數
} Gamer;

void gamer_add_card(Gamer *g, int card);
int gamer_score(const Gamer *g);

/* game.h - 遊戲流程 */
typedef enum { PLAYER, DEALER } Role;
void game_21(void);  // 單局遊戲入口

/* ui.h - 交互界面 */
void ui_show_card(int card);
void ui_show_score(const char *role, int score);
int ui_ask_hit(void);  // 返回1要牌,0停牌

關鍵改進點

  1. 狀態封裝:玩家分數計算內聚到Gamer結構體,外部不可直接修改low/high
  2. 單一職責:poker_draw只負責發牌,不處理分數;gamer_add_card只更新分數,不輸出
  3. 可測試性:核心邏輯無I/O依賴,可編寫單元測試驗證gamer_score邊界情況
  4. 擴展性:新增Poker結構體支撐多副牌、洗牌算法替換等擴展

重構後主流程對比

/* 重構前:200+行纏繞在main中 */
int main() {
    // 初始化、發牌、玩家操作、莊家邏輯、判勝負全在這裡
}

/* 重構後:清晰的聲明式流程 */
void game_21(void) {
    Poker deck; poker_init(&deck);
    Gamer player = {0}, dealer = {0};
    
    // 莊家首張
    gamer_add_card(&dealer, poker_draw(&deck));
    ui_show_score("莊家", gamer_score(&dealer));
    
    // 玩家階段
    gamer_add_card(&player, poker_draw(&deck));
    while (ui_ask_hit()) {
        gamer_add_card(&player, poker_draw(&deck));
        if (gamer_score(&player) > 21) break;
    }
    
    // 莊家階段
    while (gamer_score(&dealer) < 17) {
        gamer_add_card(&dealer, poker_draw(&deck));
    }
    
    // 判勝負
    ui_declare_winner(gamer_score(&player), gamer_score(&dealer));
}

下一步優化方向

下一篇將引入狀態機模式管理遊戲階段,實現GameState枚舉驅動的事件循環,進一步分離UI與邏輯,支撐圖形界面或網絡對戰擴展。同時將引入ctype.h標準化輸入處理,修復原代碼getAns()的緩衝區殘留缺陷。

重構不是為了炫技,而是為了讓代碼「可讀、可測、可改」。經典教材的代碼往往停留在「教會語法」層面,工程化改造才是通往專業開發的必經之路。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved