[設計案例] 生命遊戲#2, OOP版的範例程式

[設計案例] 生命遊戲#2, OOP版的範例程式

摘要提示

  • 目標:以物件導向方式實作康威生命遊戲,強調結構設計而非僅求出結果。
  • 類別設計:核心類別為 World(世界)與 Cell(細胞),分別負責環境與個體行為。
  • 設計圖:以類別圖思考介面與關係,略過 Use Case,聚焦類別責任。
  • 主程式流程:建立世界、逐世代更新、每代暫停 1 秒並印出地圖。
  • World 職責:封裝二維棋盤、提供 GetCell 與 ShowMaps 等對外介面。
  • Cell 職責:封裝細胞狀態、鄰居搜尋與狀態轉移(OnNextStateChange)。
  • 規則實作:依鄰居數決定死亡、存活、復活,對應生命遊戲四條基本規則。
  • 封裝應用:將演算法與資料隱藏於類別內,簡化主程式並提升可讀性。
  • 可擴充性:當規則變複雜或多型需求增加時,OOP 架構優勢會更明顯。
  • 原始碼:提供 C# 範例與 GitHub 連結,便於參考與實驗。

全文重點

本文展示以 C# 與物件導向設計(OOP)實作康威生命遊戲的範例。作者認為生命模擬非常適合用 OOP 實踐,能涵蓋封裝、繼承、多型與動態連結等特性。然而實際搜尋多種語言範例後,發現多數實作欠缺 OOP 風格,於是著手自行設計。

設計從類別圖出發,確立兩個核心類別:World 與 Cell。World 表示有限大小的二維空間(MxN 棋盤),負責管理所有 Cell 的容器、初始化、座標存取(GetCell)與狀態視覺化(ShowMaps)。Cell 代表單一細胞,擁有其在世界中的位置、存活狀態與鄰居搜尋方法,並透過 OnNextStateChange 根據鄰居數量執行狀態轉移。此設計將邏輯與資料適度封裝,讓主程式能以簡潔流程驅動演化。

主程式建立 World 後,以雙層迴圈逐一取得每個 Cell,呼叫 OnNextStateChange 完成一代演化,並於每代間隔 1 秒更新顯示,直至達到預設代數。World 內部本質上是包裝一個 Cell 的二維陣列,提供邊界檢查與放置(PutOn)等協調功能;ShowMaps 則以主控台輸出視覺化地圖(●/○)。

Cell 內部實作了鄰居搜尋(八方位),並將生命遊戲四大規則具體化:孤單死亡(鄰居 < 1)、擁擠死亡(鄰居 ≥ 4)、穩定存活(2 或 3)、復活(死細胞鄰居 = 3)。初始化階段還加入隨機存活機率,形成初始狀態。

作者特別強調封裝的價值:將規則與狀態管理集中在 Cell 類別,使主程式保持清爽,且 World 對外只暴露必要介面。雖然目前只是基本款規則,OOP 的好處在規則變動、更複雜行為、或引入多型時會更突出,例如未來可將 Cell 抽象化、導入不同細胞型態或環境法則。文章最後提供原始碼連結,並預告後續將變化題目以展示 OOP 架構的延展性。

段落重點

前言與設計思路

作者以生命遊戲作為 OOP 實作示例,旨在不僅完成功能,還要體現良好的程式結構。雖然網路上不乏各語言版本,但多偏程序式而非物件導向,因此自行設計。設計上從類別圖切入(略過 Use Case,因範例規模小),確立 World 與 Cell 為核心:World 作為環境容器與介面提供者,Cell 負責個體狀態與行為。此分工貼合 OOP 封裝與責任分配原則,有助清晰化各模組邊界、避免主流程臃腫,並為未來擴充打基礎。作者亦提到 VS2008 內建類別圖工具方便建模,加速原型建立與思考驗證。

Game Of Life 主程式

主程式的角色像「上帝模式」負責驅動世代演化:先設定世界尺寸與最大世代數,建立 World 後進入迴圈。每一代先呼叫 ShowMaps 輸出目前世界狀態,暫停 1 秒以利觀察,再以雙層迴圈遍歷所有座標,取得對應 Cell 並呼叫 OnNextStateChange 進行狀態轉移。此流程突顯設計的簡潔性:世代更新的核心邏輯完全由 Cell 自行決定,主程式僅負責時序與調度。如此一來,當規則變動或需要加入更多元行為時,主程式幾乎不需修改,只要擴充 Cell 或其衍生型別即可,符合開放封閉原則的精神。

World 類別設計

World 主要封裝一個二維陣列 Cell[,] 作為棋盤,並保存尺寸資訊。建構時會初始化每一格 Cell,並維護位置與世界的雙向關聯(PutOn 也會設定 Cell 的座標)。對外介面包含 GetCell(含邊界檢查,避免逾界)與 ShowMaps(以主控台顯示世界狀態,用 ● 表示活、○ 表示死)。World 的責任是資源管理與觀察呈現,不涉及演化規則,這種職責分離有助維持內聚性與降低耦合。邊界防護讓鄰居搜尋能安全運作,也預留了未來替換顯示層(例如 GUI)的可能性,只需調整 ShowMaps 或引入策略,核心模型不受影響。

Cell 類別設計與規則實作

Cell 持有對當前 World 的引用與自身座標、存活狀態;初始化時會以機率隨機決定是否存活,讓初始圖樣有多樣性。FindNeighbors 方法負責八方位鄰居搜尋,透過 World.GetCell 並自動濾除 null(越界)確保穩定。OnNextStateChange 則是規則核心:計算存活鄰居數後套用四條規則—小於 1 孤單死亡、4 以上擁擠死亡、2 或 3 維持存活、死細胞鄰居為 3 則復活。這些判斷被嚴密地封裝在 Cell 內,外界無需關心細節。此類設計也利於後續以繼承或策略改寫規則,例如不同類型 Cell 擁有差異化閾值或行為,達成多型與動態聯結。

OOP 特性與後續計畫

作者點出本例已具體展現封裝的價值:主要邏輯集中於 Cell,World 僅管理資源與輸出,主程式則保持極簡。雖然目前功能簡單,看似和一般作業參考答案差距不大,但隨著規則複雜化、引入不同生命型態、或需要抽換顯示層與演化策略時,OOP 架構的彈性與可維護性便會放大。未來可考慮抽象基類 Life、導入多型以支援多種細胞行為,或以策略模式分離規則引擎,亦可強化世界邊界規則(環形世界等)。文章最後提供 GitHub 原始碼連結以供實驗,並預告下一回將調整題目以驗證 OOP 的擴充能力。

資訊整理

知識架構圖

  1. 前置知識:學習本主題前需要掌握什麼?
    • 基本程式設計能力與流程控制(迴圈、條件判斷)
    • 物件導向程式設計概念(封裝、類別、物件、方法、屬性)
    • 陣列與座標系統(特別是二維陣列的索引操作)
    • Conway’s Game of Life 規則(鄰居計數與生死轉移)
    • C#/.NET 基本用法(Console I/O、類別與存取修飾、Random、Thread.Sleep)
  2. 核心概念:本文的 3-5 個核心概念及其關係
    • World 類別:封裝「世界」的二維空間(Cell[,]),提供存取與顯示世界狀態的介面(GetCell, ShowMaps)
    • Cell 類別:封裝「細胞」狀態與轉移邏輯(IsAlive, OnNextStateChange, FindNeighbors)
    • 主迴圈(世代演進):控制時間步(generation),遍歷世界中每個 Cell 呼叫狀態轉移
    • OOP 封裝:將規則與資料封裝於 Cell,世界管理與視覺化封裝在 World,降低主程式複雜度
    • 邊界處理與鄰居搜尋:透過 World.GetCell 的邊界檢查與 Cell.FindNeighbors 聚合鄰居資料,避免重複邏輯
  3. 技術依賴:相關技術之間的依賴關係
    • 主程式依賴 World 的建立與顯示(World() -> ShowMaps)以及存取 Cell(GetCell)
    • World 依賴 Cell 的存在(二維陣列)與狀態(IsAlive)以顯示地圖
    • Cell 依賴 World 提供位置存取(GetCell)以實作 FindNeighbors
    • 隨機初始狀態依賴 System.Random;時間節奏依賴 Thread.Sleep;輸出依賴 Console API
  4. 應用場景:適用於哪些實際場景?
    • 教學範例:示範 OOP 封裝與類別責任劃分
    • 演算法與模擬:細胞自動機、生命遊戲規則驗證
    • 軟體設計重構練習:由程序式實作重構為物件導向
    • 視覺化/互動應用雛形:後續可替換輸出層(Console -> GUI/繪圖)
    • 擴充規則與變體:加入不同生命規則、邊界策略、可配置參數

學習路徑建議

  1. 入門者路徑:零基礎如何開始?
    • 了解生命遊戲基本規則與鄰居定義(8 個鄰居)
    • 復習 C# 基礎:類別、屬性、方法、陣列、for 迴圈、if 判斷
    • 先用二維陣列寫出最簡單的狀態更新(非 OOP)
    • 將狀態顯示到 Console(簡單印出字元)
  2. 進階者路徑:已有基礎如何深化?
    • 引入 World/Cell 類別,分離責任(封裝)
    • 在 Cell 中實作 FindNeighbors 與 OnNextStateChange
    • 在 World 中實作 GetCell 的邊界檢查與 ShowMaps 的統一輸出
    • 思考架構可擴充性:抽象介面、策略模式管理規則、初始化策略
  3. 實戰路徑:如何應用到實際專案?
    • 將 Console 視圖替換為可插拔的 IRenderer(WPF/WinForms/Skia)
    • 抽離規則至 IRule(或策略)以支援多種生命規則與參數化
    • 增加世界邊界策略(封閉、環面、鏡射)與可配置大小/種子
    • 加入暫停/步進/快轉控制與資料持久化(載入/儲存版圖)

關鍵要點清單

  • World 類別職責:管理二維空間中的 Cell 與提供存取與顯示介面(GetCell/ShowMaps)(優先級: 高)
  • Cell 類別職責:封裝細胞狀態與生命規則的轉移邏輯(OnNextStateChange)(優先級: 高)
  • 封裝原則:將規則與資料放在最接近的類別(Cell),降低主程式複雜度(優先級: 高)
  • 鄰居搜尋:透過 Cell.FindNeighbors 從 World 取得 8 鄰居並處理邊界(優先級: 高)
  • 邊界檢查:World.GetCell 對座標越界回傳 null,簡化鄰居過濾(優先級: 高)
  • 主迴圈結構:以世代為單位遍歷所有 Cell 並更新狀態(優先級: 高)
  • 視覺化輸出:World.ShowMaps 使用 Console 字元(●/○)顯示狀態(優先級: 中)
  • 隨機初始化:Cell 以 Random 與機率參數設定初始存活(InitAliveProbability)(優先級: 中)
  • 時間節奏控制:Thread.Sleep 控制每個世代的顯示節奏(優先級: 低)
  • OOP 優勢:邏輯清楚、易於擴充,後續加入新規則與變體成本低(優先級: 高)
  • 規則實作:四條生命遊戲規則(孤單死、擁擠死、穩定、復活)映射到條件分支(優先級: 高)
  • 類別關係:Cell 依賴 World 存取鄰居,World 依賴 Cell 顯示狀態(優先級: 中)
  • 顯示大小與座標:Console 視窗尺寸與 SetCursorPosition 需與世界大小對齊(優先級: 低)
  • 擴充方向:抽象規則策略、替換渲染器、邊界策略、互動操作(優先級: 中)
  • 代碼注意事項:Cell 建構式中 PosX 指派疑似錯誤(PosX = posY),須檢查與修正(優先級: 中)





Facebook Pages

AI Synthesis Contents

Edit Post (Pull Request)

Post Directory