2026年11月4日 星期三

Python、Java、C++ 物件導向比較:智慧養殖池實作

Python 程式實習 · OOP 跨語言實戰

為什麼 Python、Java、C++ 都支援物件導向?

從同一個智慧養殖池範例,比較三種程式語言的 OOP 設計

當生成式 AI 能在幾秒鐘內將 Python 程式轉換成 Java 或 C++,大學生還需要學習物件導向嗎?答案是需要。因為真正值得學習的,不是記住三種語言的語法,而是理解資料如何組織、功能如何分工、系統如何擴充。

一、從智慧養殖池提出共同需求

假設要管理兩座養殖池:一般池與智慧池。每座池子有名稱與溶氧值,能回報狀態;智慧池另外在溶氧低於示範門檻時加上警示文字。三種語言都使用同一套需求,才方便比較。

教學提醒:本文的 5 mg/L 僅用於示範條件判斷,不是所有魚種或養殖環境通用的操作標準;真實監控須依物種、場域及感測器規格設定。

二、先理解共同的 OOP 架構

我們設計基底類別 Pond,再設計繼承它的 SmartPond。兩者都能呼叫 status(),但智慧池會覆寫這個方法。

Pondstatus()SmartPond覆寫 status()同一個 pond.status() 呼叫 → 依實際物件執行不同版本

這個架構展現三個核心觀念:類別組織資料與行為、繼承重用共同設計、多型讓相同呼叫依物件類型產生不同結果。封裝與抽象則進一步幫助我們管理資料規則和公開介面。

三、Python:用簡潔語法表達設計

Python 使用 class 宣告類別,__init__ 初始化資料,super() 呼叫父類別方法。方法覆寫不需要特別宣告關鍵字。

class Pond:
    def __init__(self, name, oxygen):
        self.name = name
        self.oxygen = oxygen

    def status(self):
        return f"{self.name}:溶氧 {self.oxygen:.1f} mg/L"

class SmartPond(Pond):
    def status(self):
        base = super().status()
        return base + ("(示範警示)" if self.oxygen < 5 else "(正常)")

ponds = [Pond("一般池", 6.2), SmartPond("智慧池", 4.3)]
for pond in ponds:
    print(pond.status())

四、Java:用明確的型別與繼承表達設計

Java 使用 extends 繼承,@Override 清楚標示覆寫方法。範例存成 Main.java,可使用 javac Main.java 編譯,再以 java Main 執行。

class Pond {
    protected String name;
    protected double oxygen;

    Pond(String name, double oxygen) {
        this.name = name;
        this.oxygen = oxygen;
    }

    String status() {
        return String.format("%s:溶氧 %.1f mg/L", name, oxygen);
    }
}

class SmartPond extends Pond {
    SmartPond(String name, double oxygen) {
        super(name, oxygen);
    }

    @Override
    String status() {
        return super.status() +
            (oxygen < 5 ? "(示範警示)" : "(正常)");
    }
}

public class Main {
    public static void main(String[] args) {
        Pond[] ponds = {
            new Pond("一般池", 6.2),
            new SmartPond("智慧池", 4.3)
        };
        for (Pond pond : ponds) {
            System.out.println(pond.status());
        }
    }
}

五、C++:以 virtual 支援執行階段多型

C++ 透過 public Pond 繼承,並用 virtual 與 override 實現執行階段多型。範例使用 unique_ptr 管理物件生命週期,避免手動 delete。需使用 C++14 或更新版本,例如 g++ -std=c++14 main.cpp -o main。

#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <sstream>
#include <iomanip>
using namespace std;

class Pond {
protected:
    string name;
    double oxygen;
public:
    Pond(string name, double oxygen)
        : name(name), oxygen(oxygen) {}
    virtual ~Pond() = default;

    virtual string status() const {
        ostringstream out;
        out << name << ":溶氧 " << fixed << setprecision(1)
            << oxygen << " mg/L";
        return out.str();
    }
};

class SmartPond : public Pond {
public:
    using Pond::Pond;
    string status() const override {
        return Pond::status() +
            (oxygen < 5 ? "(示範警示)" : "(正常)");
    }
};

int main() {
    vector<unique_ptr<Pond>> ponds;
    ponds.push_back(make_unique<Pond>("一般池", 6.2));
    ponds.push_back(make_unique<SmartPond>("智慧池", 4.3));
    for (const auto& pond : ponds) {
        cout << pond->status() << '\n';
    }
}

六、三種語言執行結果

上述三個程式的輸出概念相同:

一般池:溶氧 6.2 mg/L
智慧池:溶氧 4.3 mg/L(示範警示)

即使語法差異很大,背後的設計仍然一致:先定義共同介面,再允許不同物件提供自己的實作。

七、重要語法比較

概念PythonJavaC++
定義類別class Pond:class Pond { }class Pond { };
建構與初始化__init__建構子 Pond(...)建構子及初始化列表
繼承SmartPond(Pond)extends Pond: public Pond
呼叫父類別super()superPond::
覆寫方法同名方法@Overridevirtual / override
多型容器物件串列Pond[]vector<unique_ptr<Pond>>
記憶體管理自動管理為主垃圾回收為主RAII/智慧指標

八、封裝與抽象:三種語言有何不同?

本文範例主要聚焦類別、繼承與多型。若要強化封裝,可進一步限制溶氧資料的修改方式。Python 常用底線慣例與 @property;Java 可用 private 搭配方法;C++ 可用 private 或 protected 搭配成員函式。三種語言也都能透過公開方法或抽象介面,讓使用者不必了解內部實作細節。

重要觀念:Python 的 _oxygen 是命名慣例,不等同於 Java 或 C++ 的語言層級 private 存取限制。

九、AI 時代為什麼仍要學跨語言 OOP?

AI 可以快速翻譯語法,但不能代替學生確認設計是否符合需求。例如:智慧池真的應該繼承一般池嗎?溶氧值由誰負責驗證?如果加入魚菜共生池,現有的監控迴圈需不需要修改?這些都是架構設計問題,而非單純的語法轉換。

十、No-AI → AI Pair → Challenge AI 課堂實習

階段一|No-AI:觀察與比較

  1. 找出三種語言的類別、建構子、繼承與覆寫位置。
  2. 預測兩座池子的輸出,並說明多型如何發生。
  3. 將智慧池的溶氧改成 5.8,觀察結果。

階段二|AI Pair:合作轉換與檢查

請 AI 將 Python 版本改寫成 Java 或 C++,但要求保留原有類別架構。學生需逐項檢查建構子、覆寫、輸出及 C++ 記憶體管理,而不是只確認程式可以執行。

階段三|Challenge AI:新增魚菜共生池

新增 AquaponicsPond,除了溶氧還有水位;要求沿用同一個 status() 介面。學生先畫類別圖,再請 AI 協助撰寫三種語言版本,最後設計測試案例驗證。

十一、課後反思

  1. 如果未來改用 C# 或 Kotlin,哪些 OOP 觀念仍然適用?
  2. 為什麼 C++ 需要注意 virtual 與物件生命週期?
  3. 如果養殖池監控加入 MQTT 感測器,感測器應該繼承 Pond,還是讓 Pond 持有感測器物件?為什麼?

十二、結語:語法會改變,設計能力可以帶著走

Python、Java、C++ 的寫法不同,但它們都能用類別與物件組織系統,也能透過繼承與多型降低重複設計。當 AI 能替我們轉換程式語言時,理解問題、規劃責任、選擇合適的物件關係,以及驗證程式行為,才是大學生應該建立的核心能力。

教學定位:Python 程式實習/物件導向整合與跨語言比較。本文程式為可獨立執行的示範版本,未連接真實感測器。

目前有哪些程式語言支援物件導向程式設計?

PYTHON 程式實習・物件導向延伸閱讀

目前有哪些程式語言支援物件導向程式設計?

從 Python、Java、C++ 到 JavaScript、Go、Rust:比較不同語言如何組織資料與行為

學會 Python 的類別(Class)、繼承(Inheritance)、多型(Polymorphism)與封裝(Encapsulation)之後,學生可能會問:只有 Python 能寫物件導向程式嗎?換成 Java、C++ 或 JavaScript,還能使用相同的設計觀念嗎?

答案是:目前多數主流程式語言都支援某種形式的物件導向設計。不過,各語言的物件模型不完全相同,也不是每種語言都提供傳統的類別繼承。

本篇學習目標:認識常見 OOP 語言、比較類別式與原型式物件模型、理解「沒有類別繼承」不等於「不能做物件導向設計」,並透過智慧養殖池範例跨語言比較。

一、什麼是物件導向程式設計(OOP)?

物件導向程式設計(Object-Oriented Programming)是一種軟體設計方式,強調把相關的資料(狀態)與操作(行為)組織成物件,並透過明確的介面進行互動。

例如,一座智慧養殖池可以有名稱、溶氧濃度、水溫等資料,也可以提供顯示狀態、更新感測值、判斷是否警示等操作。

Pond 智慧養殖池資料(狀態)name、oxygen、temperature方法(行為)show()、check_status()感測器 / 使用者監控畫面 / 警示

圖一:將養殖池的資料與操作集中在物件內,是理解 OOP 的第一步。

二、常見支援物件導向的程式語言

程式語言OOP 主要方式常見應用
Python類別式,支援多重繼承AI、資料分析、IoT、網站
Java類別式,單一類別繼承、介面企業系統、Android
C++類別式,支援多重繼承系統、遊戲、嵌入式
C#類別式,單一類別繼承、介面.NET、Unity、企業系統
JavaScript原型式,提供 class 語法網頁、Node.js
TypeScriptJavaScript 物件模型+型別系統大型前後端應用
PHP類別式,介面與 Trait網站後端
Ruby類別式,Mixin網站與自動化
Swift類別、結構與協定iOS、macOS
Kotlin類別式,介面Android、後端
Dart類別式,MixinFlutter App
Scala物件導向與函數式混合JVM、大數據
GoStruct、方法、介面;無類別繼承雲端、網路服務
RustStruct、impl、Trait;無類別繼承系統、嵌入式
Visual Basic .NET類別式.NET 應用

其他具有物件導向能力的語言還包括 Smalltalk、Eiffel、Delphi(Object Pascal)、Objective-C 等。重點不在背誦語言名單,而是辨識每種語言使用什麼機制來組織資料、共用行為與實現多型。

三、所有語言都有 OOP 四大觀念嗎?

課堂上常用「封裝、繼承、多型、抽象」來整理 OOP;但這四項是設計概念,不代表每種語言都必須提供相同的語法。

語言封裝傳統類別繼承多型抽象機制
Python慣例、property有有ABC、Protocol 等
Java存取修飾詞有有抽象類別、介面
C++存取修飾詞有有抽象類別等
C#存取修飾詞、屬性有有抽象類別、介面
JavaScript私有欄位等原型繼承有物件介面與慣例
Go套件可見性沒有有介面
Rust模組可見性沒有有Trait
重要提醒:JavaScript 的 class 是建立在原型機制上的語法;Go 與 Rust 沒有傳統類別繼承,但可以透過介面、Trait 與組合達到許多相似的設計目標。Python 的單底線與雙底線也不是 Java 那種嚴格的 private 存取控制。

四、同一個智慧養殖池,換不同語言怎麼寫?

以下只展示「建立物件並顯示名稱」的最小範例,方便比較語法。各語言的執行環境與完整專案設定不同。

範例一:Python
class Pond:
    def __init__(self, name):
        self.name = name

    def show(self):
        print(self.name)

pond = Pond("水井一號池")
pond.show()
範例二:Java(完整可執行範例)
class Pond {
    String name;

    Pond(String name) {
        this.name = name;
    }

    void show() {
        System.out.println(name);
    }
}

public class Main {
    public static void main(String[] args) {
        Pond pond = new Pond("水井一號池");
        pond.show();
    }
}
範例三:JavaScript
class Pond {
    constructor(name) {
        this.name = name;
    }

    show() {
        console.log(this.name);
    }
}

const pond = new Pond("水井一號池");
pond.show();
範例四:Go(Struct + Method)
package main

import "fmt"

type Pond struct {
    Name string
}

func (p Pond) Show() {
    fmt.Println(p.Name)
}

func main() {
    pond := Pond{Name: "水井一號池"}
    pond.Show()
}

這四個範例都會顯示:

水井一號池

雖然語法不同,但共同的設計思路是:讓資料與相關操作保持關聯。Python、Java 與 JavaScript 使用 class;Go 則使用 struct 搭配方法。

五、沒有類別繼承,也能做到多型嗎?

可以。以 Go 為例,若不同裝置都提供 Show() 方法,就能透過介面以一致方式操作:

package main

import "fmt"

type Displayable interface {
    Show()
}

type Pond struct { Name string }
func (p Pond) Show() { fmt.Println("養殖池:", p.Name) }

type Sensor struct { Name string }
func (s Sensor) Show() { fmt.Println("感測器:", s.Name) }

func display(item Displayable) {
    item.Show()
}

func main() {
    display(Pond{Name: "水井一號池"})
    display(Sensor{Name: "溶氧感測器"})
}

這段程式沒有使用類別繼承,但 display() 可以接受不同型別的物件,展現多型設計。這也提醒我們:學習 OOP 的核心是理解設計概念,而非只記住某種語法。

六、哪些語言不是以 OOP 為主要方式?

C

主要是程序式語言,沒有內建 class 與類別繼承,但可用 struct、函式與指標模擬部分 OOP 設計。

Haskell

主要是函數式語言,使用 Typeclass 等機制表達抽象與多型,不是傳統類別式 OOP。

SQL

主要用於資料查詢與操作,不是一般用途的物件導向程式語言。

也要避免把「不以 OOP 為主」理解成「比較落後」。不同語言與典範各有擅長的問題領域。

七、為什麼大學生適合先用 Python 學 OOP?

  1. 語法相對簡潔:能先專注於類別、物件、繼承與多型,而不是大量樣板程式碼。
  2. 容易連結生活情境:飲料店 POS、學生選課、智慧養殖池都能用同一套設計概念來分析。
  3. 方便延伸到實務:可以從 Pond、Sensor、Controller 出發,逐步加入 MQTT、Web API、資料庫與 AIoT。

當學生理解 Python 的 OOP 設計後,學習 Java、C++ 或 JavaScript 時,就可以比較「概念相同在哪裡、語法與限制不同在哪裡」。

八、AI 時代的三階段學習活動

第一階段:No-AI|先自己比較

不使用 AI,閱讀 Python 與 JavaScript 的 Pond 範例,圈出「屬性」、「建構流程」、「方法」與「建立物件」的位置,並說明兩者相似之處。

第二階段:AI Pair|請 AI 協助跨語言轉換

建議提示詞:「請把這段 Python Pond 類別改寫成 JavaScript 與 Java,保留相同功能,並逐行比較建構子、屬性、方法與物件建立方式。請提供可執行的完整範例。」

學生應實際執行、核對輸出,不應只因為 AI 產生了程式碼就認為轉換成功。

第三階段:Challenge AI|檢查設計是否合理

假設系統增加「智慧養殖池、魚菜共生池、溶氧感測器」三種物件,請學生先判斷哪些適合用繼承、哪些適合用組合,再請 AI 提出架構,最後比較其合理性。

思考問題:感測器是養殖池的一種(is-a),還是養殖池擁有的設備(has-a)?如果是後者,通常應優先考慮組合,而非硬套繼承。

九、課後練習

  1. 列出三種支援類別式 OOP 的語言,說明各自常見的應用。
  2. 為什麼 JavaScript 雖然有 class,仍常被稱為原型式語言?
  3. Go 沒有傳統類別繼承,為什麼仍可以實現多型?
  4. 把 Python 的 Pond 類別新增 oxygen 屬性與 check_status() 方法,再嘗試改寫為另一種語言。
  5. 請說明「物件導向是一種設計方法,而非某個語言的專利」的意思。

十、結語:真正重要的是跨語言的設計能力

物件導向不是 Python 的專利,也不等於每一個程式都必須使用 class。 學會辨識資料、行為、介面與物件間的關係,才有能力在不同程式語言之間選擇合適的設計方式。即使 AI 能快速翻譯程式碼,學生仍需要判斷轉換後的程式是否符合原語言的特性與實際需求。

延伸閱讀建議:下一篇可使用同一個智慧養殖池案例,深入比較 Python、Java 與 C++ 的類別、封裝、繼承及多型。

2026年11月2日 星期一

Python 例外處理實戰:飲料店 POS 點餐系統

Python 程式實習|整合實作

Python 例外處理實戰:打造不怕輸入錯誤的飲料店 POS 點餐系統

顧客可能輸入錯誤的商品代碼、把「兩杯」當成數字、輸入負數杯數,甚至付款不足。真正可靠的程式,不是永遠不會遇到錯誤,而是能辨識可預期的錯誤,給出提示並讓使用者重新操作。本篇從生活情境出發,整合字典、迴圈、函式、類別、例外處理,完成可在終端機執行的飲料店 POS。

一、情境與學習目標

店裡販售紅茶(30 元)、珍珠奶茶(50 元)、綠茶(35 元)。顧客可重複選購商品、輸入杯數,輸入 0 結帳;系統列出明細、計算總額、收款及找零。

完成後,你應能回答:哪些錯誤應使用 try/except?哪些情況要用 if 驗證?為什麼把輸入流程與訂單資料分開設計?

二、先看系統流程

顯示菜單輸入代碼驗證資料加入訂單結帳收款輸入錯誤 → 重新輸入繼續點餐 → 回到菜單

三、第一步:用字典建立菜單

MENU = {"1": ("紅茶", 30),
        "2": ("珍珠奶茶", 50),
        "3": ("綠茶", 35)}

商品代碼使用字串,商品資料以 (名稱, 單價) 表示。透過 MENU[code] 可以找到對應商品;如果代碼不存在,應該提供清楚的錯誤訊息,而不是讓程式直接崩潰。

四、第二步:先處理最常見的輸入錯誤

def read_quantity():
    while True:
        try:
            quantity = int(input("數量(杯):"))
            if quantity <= 0:
                raise ValueError("數量必須大於 0")
            return quantity
        except ValueError as error:
            print(f"輸入錯誤:{error},請重新輸入。")

int() 遇到「兩杯」會產生 ValueError;數量為 0 或負數則是我們定義的商業規則,使用 raise ValueError(...) 主動拒絕。兩者都由同一個 except 處理,再用迴圈重新詢問。

五、第三步:用類別管理訂單

class Order:
    def __init__(self):
        self.items = []

    def add_item(self, code, quantity):
        if code not in MENU:
            raise KeyError("找不到這個商品代碼")
        if quantity <= 0:
            raise ValueError("數量必須大於 0")
        name, price = MENU[code]
        self.items.append((name, price, quantity))

    def total(self):
        return sum(price * qty for _, price, qty in self.items)

Order 負責保存訂單和計算金額;add_item() 再次檢查資料,避免其他程式繞過輸入介面時加入不合理的商品。這就是封裝與例外處理的結合。

六、第四步:結帳時處理付款不足

def checkout(order):
    total = order.total()
    while True:
        try:
            paid = int(input(f"應收 {total} 元,請輸入實收金額:"))
            if paid < total:
                raise ValueError("實收金額不足")
        except ValueError as error:
            print(f"付款失敗:{error},請重新輸入。")
        else:
            print(f"找零:{paid - total}元")
            print("交易完成,謝謝光臨!")
            return

這裡的 else 只有在 try 沒有產生例外時才執行。實收金額不足時不會結帳,而是要求重新輸入。這是課堂示範的現金收款模型,尚未包含折扣、發票或交易紀錄。

七、完整可執行程式

將以下程式存成 drink_pos.py,在終端機輸入 python drink_pos.py 執行。不需要額外安裝套件。

"""飲料店 POS:以教學為目的的命令列範例。"""
MENU = {"1": ("紅茶", 30), "2": ("珍珠奶茶", 50), "3": ("綠茶", 35)}

class Order:
    def __init__(self):
        self.items = []

    def add_item(self, code, quantity):
        if code not in MENU:
            raise KeyError("找不到這個商品代碼")
        if quantity <= 0:
            raise ValueError("數量必須大於 0")
        name, price = MENU[code]
        self.items.append((name, price, quantity))

    def total(self):
        return sum(price * qty for _, price, qty in self.items)

    def show_receipt(self):
        print("\n=== 訂單明細 ===")
        for name, price, qty in self.items:
            print(f"{name} {qty}杯 × {price}元 = {price * qty}元")
        print(f"合計:{self.total()}元")


def read_quantity():
    while True:
        try:
            quantity = int(input("數量(杯):"))
            if quantity <= 0:
                raise ValueError("數量必須大於 0")
            return quantity
        except ValueError as error:
            print(f"輸入錯誤:{error},請重新輸入。")


def checkout(order):
    total = order.total()
    while True:
        try:
            paid = int(input(f"應收 {total} 元,請輸入實收金額:"))
            if paid < total:
                raise ValueError("實收金額不足")
        except ValueError as error:
            print(f"付款失敗:{error},請重新輸入。")
        else:
            print(f"找零:{paid - total}元")
            print("交易完成,謝謝光臨!")
            return


def main():
    order = Order()
    print("=== Python 飲料店 POS ===")
    while True:
        print("\n菜單:")
        for code, (name, price) in MENU.items():
            print(f"{code}. {name} {price}元")
        code = input("商品代碼(0 結帳):").strip()
        if code == "0":
            if not order.items:
                print("目前沒有商品,結束程式。")
                return
            break
        try:
            if code not in MENU:
                raise KeyError("沒有這個商品代碼")
            qty = read_quantity()
            order.add_item(code, qty)
        except KeyError as error:
            print(f"選購失敗:{error.args[0]},請重新選擇。")
        else:
            print("商品已加入訂單。")
    order.show_receipt()
    checkout(order)


if __name__ == "__main__":
    main()

八、執行測試:讓錯誤真的發生

=== Python 飲料店 POS ===
菜單:
1. 紅茶 30元
2. 珍珠奶茶 50元
3. 綠茶 35元
商品代碼(0 結帳):9
選購失敗:沒有這個商品代碼,請重新選擇。

商品代碼(0 結帳):2
數量(杯):兩杯
輸入錯誤:invalid literal for int() with base 10: '兩杯',請重新輸入。
數量(杯):-1
輸入錯誤:數量必須大於 0,請重新輸入。
數量(杯):2
商品已加入訂單。

商品代碼(0 結帳):0
=== 訂單明細 ===
珍珠奶茶 2杯 × 50元 = 100元
合計:100元
應收 100 元,請輸入實收金額:50
付款失敗:實收金額不足,請重新輸入。
應收 100 元,請輸入實收金額:200
找零:100元
交易完成,謝謝光臨!

上方為示意互動紀錄,實際終端機畫面會依輸入而異。

九、程式中的例外處理與條件判斷

情境處理方式原因
輸入「兩杯」except ValueErrorint() 無法轉換
輸入 -1 杯if + raise ValueError違反購買數量規則
商品代碼 9raise KeyError + except菜單沒有此商品
付款不足if + raise ValueError違反付款規則
正常付款else沒有發生例外才完成交易
重要觀念:不是所有錯誤都應該用 except 處理。程式設計時,先以條件判斷描述正常的業務規則,再用例外表示無法完成操作的情況。也不要使用 except: pass 把問題全部隱藏。

十、三階段 AI 協作實習

No-AI|獨立理解

不使用 AI,畫出點餐流程,指出至少三個可能發生錯誤的地方,並說明 while、try、except 各自負責什麼。

AI Pair|共同改善

請 AI 協助新增「同商品累計杯數」及「輸入 q 取消交易」功能。學生必須自行測試空訂單、非法代碼與取消操作。

Challenge AI|驗證 AI

要求 AI 將 POS 擴充成「庫存有限」的版本。測試庫存不足、輸入 0 杯、超額付款與連續輸入錯誤,並檢查是否發生先扣庫存、後來卻結帳失敗的資料不一致問題。

十一、延伸挑戰與教學反思

本範例刻意保持精簡,讓學生先理解錯誤發生時如何恢復。下一步可增加商品庫存、訂單編號、檔案儲存、GUI 或 Django 網頁介面。正式 POS 還需要考慮金額精度、資料持久化、交易一致性與權限管理。

AI 可以快速寫出 try/except,但真正的程式設計能力,是判斷什麼情況應重試、拒絕、取消或回復資料。當學生能清楚回答這些問題,就不只是會寫語法,而是開始學會設計可靠的系統。

Python 程式實習|生活化教學 × 物件導向 × 例外處理 × AI 協作

Python 例外處理:從飲料店點餐學會 try、except

Python 程式實習|生活情境 × AI 協作

Python 例外處理(Exception Handling):當程式發生錯誤,如何讓它繼續運作?

從飲料店買珍珠奶茶、朋友聚餐分帳,到智慧養殖感測器,逐步理解 try、except、else、finally 與 raise。

一、生活情境:到飲料店買珍珠奶茶

每杯珍珠奶茶 50 元,請顧客輸入購買杯數:

price = 50
quantity = int(input("請問要買幾杯珍珠奶茶?"))
total = price * quantity
print("總金額:", total, "元")

輸入 3,結果是 150 元;但輸入「三杯」會發生 ValueError,因為 int() 無法將這段文字轉成整數。

重點:輸入資料不符合程式預期,不應讓整個點餐流程毫無說明地中斷。

二、第一個例外處理:try 與 except

price = 50
try:
    quantity = int(input("請問要買幾杯珍珠奶茶?"))
    total = price * quantity
    print("總金額:", total, "元")
except ValueError:
    print("輸入錯誤!請輸入數字。")

try 放可能產生例外的程式;except ValueError 負責處理指定的轉換錯誤。其他未被捕捉的例外仍可能中斷程式。

try:執行程式發生 ValueError?否是正常完成 tryexcept:處理錯誤

三、輸入錯誤就重新輸入

加入迴圈,讓顧客可以重新輸入。注意:輸入負數不會自動產生 ValueError,必須另外檢查。

price = 50
while True:
    try:
        quantity = int(input("請問要買幾杯?"))
        if quantity <= 0:
            print("購買數量必須大於 0")
            continue
        break
    except ValueError:
        print("請輸入正確的整數!")

total = price * quantity
print("總金額:", total, "元")
請問要買幾杯?三杯
請輸入正確的整數!
請問要買幾杯?-2
購買數量必須大於 0
請問要買幾杯?3
總金額: 150 元

四、聚餐分帳:認識不同例外

如果 1,200 元餐費要平均分攤,輸入「四個人」會有轉換問題,輸入 0 則會發生除以零的例外。

total = 1200
try:
    people = int(input("請輸入分帳人數:"))
    if people < 0:
        print("人數不能是負數")
    else:
        average = total / people
        print("每人應付:", average)
except ValueError:
    print("請輸入整數!")
except ZeroDivisionError:
    print("分帳人數不能是 0!")
例外典型原因生活情境
ValueError值無法正確轉換杯數輸入「三杯」
ZeroDivisionError除以零分帳人數輸入 0
FileNotFoundError指定檔案不存在找不到成績檔
IndexError索引超出範圍讀取不存在的第 5 筆資料
KeyError字典沒有指定鍵查詢不存在的商品代碼
TypeError運算型別不相容字串與整數直接相加

五、讀取成績檔:FileNotFoundError

try:
    with open("scores.txt", "r", encoding="utf-8") as file:
        content = file.read()
        print(content)
except FileNotFoundError:
    print("找不到成績檔案!")

with open() 能在離開區塊時自動關閉檔案,比手動呼叫 close() 更可靠。

六、try、except、else、finally

try:
    number = int(input("請輸入一個數字:"))
except ValueError:
    print("輸入錯誤!")
else:
    print("輸入成功:", number)
finally:
    print("程式處理結束")
語法作用
try執行可能產生例外的程式
except捕捉並處理指定例外
else當 try 沒有發生例外時執行
finally離開處理結構時通常會執行,適合清理資源

七、raise:自己主動提出例外

當輸入數值雖然是整數,卻不符合商業規則時,可使用 raise。

def order_drink(quantity):
    if quantity <= 0:
        raise ValueError("購買數量必須大於 0")
    return quantity * 50

try:
    total = order_drink(-2)
    print("總金額:", total)
except ValueError as error:
    print("訂購失敗:", error)
訂購失敗: 購買數量必須大於 0

八、整合物件導向:智慧養殖感測資料檢查

延續前面 Class 與封裝的課程,讓養殖池物件檢查溶氧數值。此處使用 None 表示尚未取得有效資料。

class Pond:
    def __init__(self, name):
        self.name = name
        self.oxygen = None

    def update_oxygen(self, value):
        oxygen = float(value)
        if oxygen < 0:
            raise ValueError("溶氧不能小於 0")
        self.oxygen = oxygen

pond1 = Pond("水井一號池")
try:
    pond1.update_oxygen("-5")
except ValueError as error:
    print("感測資料錯誤:", error)
print("目前溶氧:", pond1.oxygen)
感測資料錯誤: 溶氧不能小於 0
目前溶氧: None

這裡的數值與規則只是教學示例。正式 AIoT 系統還需要依感測器規格、現場環境及養殖需求進行驗證。

九、AI 協作實習:No-AI、AI Pair、Challenge AI

No-AI:預測錯誤

先不使用 AI,測試下列程式輸入 ABC、0 與有效整數的結果,並自行加入例外處理。

a = int(input("第一個數字:"))
b = int(input("第二個數字:"))
print(a / b)

AI Pair:請 AI 協助改善

提示詞:「請幫我設計一個 Python 飲料店點餐程式,使用 try、except、else、finally,處理輸入非整數、購買數量小於等於零等情況。請逐步解釋語法,並保持程式簡單。」

Challenge AI:最多輸入錯誤三次

新增需求:顧客最多可以輸入錯誤三次,第三次失敗即取消訂單。先自行設計流程,再與 AI 合作完成。測試第一次成功、錯誤兩次後成功、連續錯誤三次等情況。

十、例外處理不是隱藏錯誤

不要隨意使用沒有指定類型的 except:,更不要用 except: pass 靜默忽略所有問題。應優先捕捉預期的例外,提供清楚訊息;無法安全恢復的錯誤應適當記錄或向上傳遞。

核心觀念:例外處理不是保證程式永遠不出錯,而是讓程式在預期的錯誤發生時,能採取清楚、可控制的應對方式。

十一、結語:從生活中的錯誤,設計更可靠的程式

飲料店點餐、聚餐分帳、成績檔案與智慧養殖感測器,都是理解例外處理的好起點。AI 可以快速寫出 try 與 except,但學生仍需要判斷哪些錯誤可以恢復、哪些應該停止,以及哪些不能被忽略。

下一個實習:Python 例外處理實戰——打造不怕輸入錯誤的飲料店 POS 點餐系統。

2026年11月1日 星期日

Capstone 問題診斷導向教學:Gate A、Gate B、Gate C 三大決策關卡

CAPSTONE PROJECT · PROBLEM DIAGNOSIS

從議題探索到專題成立:認識 Gate A、Gate B、Gate C 三大決策關卡

問題診斷導向 Capstone 教學文章|適用於不同產業、組織、社區及跨域合作場域

在 Capstone(總整課程/專題實作)中,學生常急著決定要開發網站、App、智慧設備或 AI 應用,卻未必先確認真實工作情境中的問題是否存在、為何持續發生,以及是否適合在課程期間完成。問題診斷導向的核心,就是先以訪談、觀察與工作證據理解問題,再把經過驗證的問題轉化為可執行的專題。

為避免各階段過早跳到解決方案,教學流程設置三個階段性決策關卡(Decision Gates):Gate A、Gate B、Gate C。每一關都要回答不同的問題,且未通過時應補足資料或調整方向,而不是直接進入下一步。

一句話記憶:Gate A 選對議題;Gate B 找對問題;Gate C 確認專題能夠執行與驗證。

一、三個 Gate 有什麼不同?

比較項目Gate AGate BGate C
核心提問這個議題值得進一步探索嗎?是否找到有證據支持的跨角色系統性核心問題?能否轉成學生可執行、可驗證的課程專題?
主要依據高階訪談、問題線索、候選議題比較與合作條件執行層訪談、旅程圖、工作流程圖、關鍵資訊流圖與六要素POV、介入位置、HMW、教學可行性與責任邊界
產出焦點議題與場域探索邊界跨角色系統性核心問題課程專題成立/調整判定
通過後規劃執行層訪談及場域探索萃取 Needs、Insight、POV定錨正式課程挑戰任務
未通過重新比較議題、釐清範圍或合作條件回到角色別三圖證據與問題群集調整介入範圍、任務難度、資源或責任邊界

二、Gate A:焦點議題確認

Gate A 是「選定值得深入研究的議題」,而不是宣告已經找到問題根因。在前期高階訪談後,先整理受訪者提及的具體困難、反覆現象、工作負擔或風險,形成問題線索,再依共同工作情境分群為候選議題。接著比較各議題的重要性、探索價值、教學適切性、教學可行性及場域合作可行性。

Gate A 通過的三項條件

  • 已形成明確且具探索價值的焦點議題。
  • 已界定探索範圍與合作邊界。
  • 已確認具備後續執行層訪談與場域探索的可行性。

通用示例:訪談顯示某項跨部門服務需要多次協調,於是先把「跨角色服務協作與資訊傳遞」列為焦點議題;此時仍不能斷定原因就是資訊不完整、工具不足或人員疏失。這些都要留待現場查證。

教學提醒:焦點議題是探索主題,不是系統性核心問題,更不是解決方案。

三、Gate B:跨角色系統性核心問題確認

通過 Gate A 後,教師與學生要進入實際工作情境,理解至少兩個核心角色如何共同完成任務。透過Journey Map(角色旅程圖)、Workflow Map(工作流程圖)、Information Flow Map(關鍵資訊流圖),交叉比對體驗、行為與資訊承接的證據。

接著整合六項核心問題元素:主要角色、共同關鍵情境、共同關鍵任務、共同形成機制、共同問題現象、共同影響。只有當角色別證據指向同一形成機制,才有基礎提出跨角色系統性核心問題。

Gate B 四項品質檢核

  • 兩個角色的問題群集是否指向同一跨角色形成機制?
  • 六項核心問題元素是否都有旅程圖、流程圖、資訊流圖的證據支持?
  • 核心問題是否未預設工具、系統或 AI 解方?
  • 是否可進入 Needs、Insight 與 POV?

通用示例:若兩個角色都在同一服務交接流程中反覆補問與確認,學生不能只因兩人都說「溝通麻煩」就判定為共同問題;還須查明哪個交接節點、哪種必要資訊或完成條件未被充分承接,以及實際造成什麼工作影響。

教學提醒:訪談中的推測不是已確認的形成機制;示範案例、AI 生成的情節與真實現場證據也必須清楚區分。

四、Gate B 之後:Needs、Insight 與 POV

Gate B 通過後,進入設計觀點形成階段。這三個概念有明確分工:

概念回答的問題撰寫重點
Needs(需求)角色要完成關鍵任務,缺少什麼必要工作條件?寫工作條件,不寫工具功能。
Insight(洞察)為何已有因應作法,問題仍持續發生?連結既有作法、形成機制、重複現象及影響。
POV(設計觀點)兩個角色共同需要改善的問題切面是什麼?整合核心角色、共同情境、共同需求及共同洞察。

POV 句型:【核心角色】需要在【共同關鍵情境】下,能夠【共同關鍵需求】,因為【共同洞察】。

POV 還要經過五項品質檢核:是否對應兩角色需求、說明共同形成機制、聚焦同一資訊承接鏈、未預設工具或 AI 解方,以及能否進入介入位置與 HMW。

五、Gate C:課程專題成立

經過 POV 品質檢核後,才進一步判斷應介入哪個工作環節,並形成 HMW(How Might We,我們如何能夠……?)設計問題。教材以三個可介入位置協助思考:L1 資料產生層、L2 資訊流與交接層、L3 判斷使用層。它們是設計介入位置,不是另一套問題診斷模型。

接著確認學生的學習能力、跨域整合需求、場域合作資源、資料與驗證條件,以及專業責任邊界。若使用 AI,也必須明確界定 AI 的輔助範圍,不應把 AI 視為唯一解方,更不能以學生原型取代應由專業人員負責的判斷。

Gate C 課程成立性檢核重點

  • 是否具備真實問題證據與產業/場域需求?
  • 是否具有跨角色或跨領域的學習價值?
  • 是否符合學生先備能力及課程時間?
  • 是否能完成原型、情境測試或其他可行驗證?
  • 是否具備必要的場域、人員、資料與合作支持?
  • 是否清楚界定學生、AI 與領域專業人員的責任?

通用示例:即使已確認跨角色資訊承接問題,也不能直接指定學生開發一套完整管理系統。若課程僅有一學期,可能先以流程重整、表單原型、介面模擬或情境測試進行驗證,再評估是否需要進一步導入技術。

六、完整教學流程圖

高階訪談 → 問題線索 → 候選議題比較
↓
Gate A|焦點議題是否成立?
↓
執行層訪談與場域觀察 → 三張分析圖 → 六要素核心問題
↓
Gate B|跨角色系統性核心問題是否成立?
↓
Needs → Insight → POV → POV 品質檢核
↓
介入位置 L1/L2/L3 → HMW → 課程成立性與責任邊界
↓
Gate C|課程專題是否成立?
↓
正式 Capstone 課程挑戰任務 → 原型與驗證

七、教師帶領學生時,最常見的三個誤區

  1. 把候選議題當成問題根因:高階訪談指出「流程不順」,只表示值得查證;真正形成機制仍需執行層證據。
  2. 把需求寫成工具:「需要一個 AI 平台」是可能的解方;「需要在交接時取得完整且可確認的資訊」才是工作需求。
  3. 把做得出來當成值得做:技術可行不代表專題成立;還須確認問題證據、場域價值、學生學習與驗證條件。

八、課堂討論與反思

  1. 如果合作單位希望學生「做一個 App」,教師應先用哪個 Gate 的思考方式回應?為什麼?
  2. 兩個角色都抱怨資訊傳遞麻煩,是否足以通過 Gate B?還需要哪些證據?
  3. 若核心問題已確認,但場域無法提供測試資料,Gate C 應如何調整專題範圍?

Gate A 選對議題 → Gate B 找對問題 → Gate C 做成可執行、可驗證的專題。

三個 Gate 並不是為了增加行政程序,而是讓學生每走一步都有依據:先確認值得探索,再確認問題成立,最後確認課程可做且能驗證。這也是問題診斷導向 Capstone 與「先決定技術、再尋找應用情境」最大的差異。

教材依據:《Capstone 專題問題診斷導向備課工作坊_20261031(智慧照護)_虎科場Final.pdf》。本文以跨產業、跨機構的通用教學語境重新編寫;通用示例僅作教學說明,並非特定機構的實際診斷結論。

2026年10月31日 星期六

Python 物件導向實戰:智慧養殖監控系統

Python 物件導向實戰:用 Class、繼承、多型與封裝設計一套智慧養殖監控系統

Python 程式實習系列(五)|從水井村智慧養殖情境出發,整合四大物件導向觀念,並透過 No-AI、AI Pair、Challenge AI 練習 AI 協作設計。

一、AI 已經會寫程式,為什麼還要做整合實作?

當 AI 可以一次生成完整的 Python 類別,學習目標就不應只是「打出正確語法」,而是能解釋:哪些資料需要保護?哪些功能應共用?新型養殖池加入時,舊程式是否需要修改?本篇以水井村智慧養殖為情境,從設計到測試建立可執行的入門監控模擬系統。

二、需求分析:先決定系統要做什麼

這是一個離線模擬程式,不連接真實感測器。系統要管理一般池、智慧池與魚菜共生池;各池都有名稱與溶氧。智慧池另外有溶氧警戒值,魚菜共生池再增加水位。系統必須拒絕負數溶氧、允許更新感測數據,並使用同一個迴圈列出不同類型養殖池的狀態。

三、先畫出繼承階層圖

由下圖可見,SmartPond 是 Pond 的一種;AquaponicsPond 又是 SmartPond 的一種。這是為了教學而採用的簡化架構。實務上若魚菜共生池的規則與智慧池差異很大,也可以考慮改用組合而非繼承。
Pondoxygen / check_status() SmartPondlimit / 覆寫 check_status() AquaponicsPondwater_level / 覆寫 check_status() 共同屬性與方法新增警戒規則新增水位監測

圖:教學用的三層繼承階層;三種物件都能呼叫 check_status()。

四、完整 Python 程式:四大觀念一次整合

將下方程式存成 pond_oop_demo.py,以 Python 3 執行。範例不需額外安裝套件。

class Pond:
    def __init__(self, name, oxygen):
        self.name = name
        self.oxygen = oxygen

    @property
    def oxygen(self):
        return self._oxygen

    @oxygen.setter
    def oxygen(self, value):
        if not isinstance(value, (int, float)) or isinstance(value, bool):
            raise TypeError("溶氧必須是數字")
        if not 0 <= value <= 25:
            raise ValueError("溶氧須介於 0~25 mg/L")
        self._oxygen = float(value)

    def check_status(self):
        return f"{self.name}:溶氧 {self.oxygen:.1f} mg/L"


class SmartPond(Pond):
    def __init__(self, name, oxygen, limit=5.0):
        super().__init__(name, oxygen)
        if limit < 0:
            raise ValueError("警戒值不可為負數")
        self.limit = limit

    def check_status(self):
        status = "偏低,請檢查增氧設備" if self.oxygen < self.limit else "正常"
        return f"{self.name}:溶氧 {self.oxygen:.1f} mg/L,{status}"


class AquaponicsPond(SmartPond):
    def __init__(self, name, oxygen, water_level, limit=5.0):
        super().__init__(name, oxygen, limit)
        self.water_level = water_level

    @property
    def water_level(self):
        return self._water_level

    @water_level.setter
    def water_level(self, value):
        if not isinstance(value, (int, float)) or isinstance(value, bool):
            raise TypeError("水位必須是數字")
        if value < 0:
            raise ValueError("水位不可為負數")
        self._water_level = float(value)

    def check_status(self):
        oxygen_status = super().check_status()
        level_status = "水位偏低" if self.water_level < 30 else "水位正常"
        return f"{oxygen_status};{level_status}({self.water_level:.0f} cm)"


ponds = [
    Pond("一般池", 6.1),
    SmartPond("智慧池", 4.2),
    AquaponicsPond("魚菜共生池", 5.8, 25),
]

for pond in ponds:
    print(pond.check_status())

print("\n更新智慧池溶氧後:")
ponds[1].oxygen = 5.6
print(ponds[1].check_status())

print("\n測試不合理資料:")
try:
    ponds[1].oxygen = -1
except ValueError as error:
    print("拒絕更新:", error)

五、執行結果與程式解讀

程式建立三種物件,透過相同的 check_status() 呼叫顯示不同訊息;接著更新智慧池溶氧,最後刻意輸入 -1,確認資料驗證會拒絕不合理數值。

一般池:溶氧 6.1 mg/L
智慧池:溶氧 4.2 mg/L,偏低,請檢查增氧設備
魚菜共生池:溶氧 5.8 mg/L,正常;水位偏低(25 cm)

更新智慧池溶氧後:
智慧池:溶氧 5.6 mg/L,正常

測試不合理資料:
拒絕更新: 溶氧須介於 0~25 mg/L

六、Class、繼承、多型、封裝在哪裡?

觀念程式位置設計意義
Class 類別class Pond組織名稱、溶氧與狀態查詢
Inheritance 繼承SmartPond(Pond)沿用共同資料與方法,再增加警戒功能
Polymorphism 多型pond.check_status()同一呼叫,依物件類型執行不同方法
Encapsulation 封裝@property、@oxygen.setter在公開屬性寫入時檢查資料有效性

七、No-AI → AI Pair → Challenge AI

No-AI:先預測再執行

不使用 AI,畫出三個類別的關係,指出哪一個方法被覆寫;預測三個物件的輸出,並解釋為何負數溶氧被拒絕。

AI Pair:請 AI 當程式碼審查者

提示詞:「請審查這份 Python 智慧養殖監控程式,指出繼承是否合理、封裝是否完整、多型是否有幫助,並提出三項可測試的改善建議。先不要直接重寫程式。」

Challenge AI:新增需求

新增 ShrimpPond,要求不同的溶氧警戒值;再增加溫度屬性與溫度異常警告。學生先畫類別圖、寫測試案例,再請 AI 協助實作。檢查是否能在不修改原本巡覽迴圈的情況下加入新類別。

八、進一步連接 AIoT:從模擬走向真實系統

完成本篇後,可讓 Raspberry Pi Pico W 或 ESP32 負責感測資料取得,再透過 MQTT 或 HTTP 傳送至 Python 程式。此時可以把「感測器資料來源」獨立為物件,讓養殖池類別專注於狀態與規則;避免讓一個類別同時負責網路、資料庫、感測器與警報的全部工作。

九、測試與安全邊界

本篇的溶氧 0~25 mg/L、警戒值 5 mg/L 與水位 30 cm 都是教學示例,不是現場控制建議。真實系統須依物種、設備量測範圍、校正與現場條件決定閾值;感測器斷線、資料缺漏、時間戳記與設備故障也需要另外處理。

此外,Python 的底線屬性是慣例,@property 不是防止惡意修改的安全機制。它主要協助我們維持正常程式使用情境下的資料一致性。

十、結語:AI 寫程式,學生設計系統

Class 讓我們定義物件;繼承讓相似物件共用設計;多型讓不同物件以一致方式操作;封裝則幫助維持資料合理性。真正的學習成果,不只是程式能執行,而是能回答「為什麼這樣設計?」並能在需求改變時提出合理修改。

關鍵字:Python、OOP、Class、Inheritance、Polymorphism、Encapsulation、AIoT、智慧養殖、AI 協作

Python 封裝:為什麼不應任意修改物件資料?

Python 封裝:為什麼不應任意修改物件資料?

Python 封裝(Encapsulation):為什麼不應該讓所有程式都能隨意修改物件資料?

Python 物件導向系列(四)|Class × Inheritance × Polymorphism × Encapsulation

AI 能快速寫出 Python 類別,但真正的設計能力,是知道哪些資料需要檢查、哪些操作應該被允許,以及如何讓物件維持合理的狀態。

前面三篇已介紹類別、繼承與多型。這次繼續以「水井村智慧養殖池」為例,理解封裝(Encapsulation)的用意。封裝不只是把資料藏起來,更是把資料與相關操作組織在一起,提供清楚的存取介面與檢查規則。

一、生活中的封裝:自動販賣機

使用者可以選商品、付款與購買,但不能任意修改販賣機的庫存帳務。操作必須經過規定的介面;程式中的物件也可以如此設計。

二、沒有檢查的公開屬性,會發生什麼事?

class Pond:
    def __init__(self, name, oxygen):
        self.name = name
        self.oxygen = oxygen

pond1 = Pond("水井一號池", 5.8)
pond1.oxygen = -100
print(pond1.oxygen)

執行結果:

-100

溶氧濃度不應為負數,但這段程式仍允許寫入。當感測數據用於警報或控制時,資料錯誤可能引發後續問題。

三、第一步:透過方法更新資料

class Pond:
    def __init__(self, name, oxygen):
        self.name = name
        self.oxygen = 0
        self.set_oxygen(oxygen)

    def set_oxygen(self, value):
        if value >= 0:
            self.oxygen = value
        else:
            print("錯誤:溶氧不能小於 0")

    def show_info(self):
        print(self.name, "溶氧:", self.oxygen)

pond1 = Pond("水井一號池", 5.8)
pond1.set_oxygen(6.2)
pond1.set_oxygen(-100)
pond1.show_info()
錯誤:溶氧不能小於 0
水井一號池 溶氧: 6.2

這個版本可以檢查經過 set_oxygen() 的更新,但外部仍能執行 pond1.oxygen = -100 繞過檢查,因此需要更清楚的屬性介面。

四、Python 的屬性命名與封裝

寫法意義注意事項
self.oxygen公開屬性通常可直接讀寫
self._oxygen非公開慣例提醒使用者避免從外部直接修改
self.__oxygen名稱改寫降低意外存取及子類別名稱衝突,並非絕對私有

Python 的雙底線屬性會發生 Name Mangling(名稱改寫),例如 __oxygen 可能成為 _Pond__oxygen。這不是安全邊界,也不能用來防止惡意程式存取。

五、推薦實作:使用 @property 驗證資料

class Pond:
    def __init__(self, name, oxygen):
        self.name = name
        self.oxygen = oxygen

    @property
    def oxygen(self):
        return self._oxygen

    @oxygen.setter
    def oxygen(self, value):
        if value < 0:
            raise ValueError("溶氧不能小於 0")
        self._oxygen = value

pond1 = Pond("水井一號池", 5.8)
print(pond1.oxygen)
pond1.oxygen = 6.2
print(pond1.oxygen)
5.8
6.2

此時 pond1.oxygen = 6.2 看似直接指定屬性,實際上會呼叫 @oxygen.setter 的檢查邏輯。若執行 pond1.oxygen = -100,則會引發 ValueError。

重點:_oxygen 仍可被直接存取。封裝的目的是建立可靠、清楚的操作介面,並非提供資訊安全上的絕對保護。

六、封裝流程圖:資料如何被檢查?

外部程式pond.oxygen = 值@oxygen.setter檢查 value ≥ 0合理數值更新 _oxygen不合理數值引發 ValueError

使用者只需要知道如何操作公開介面,物件內部則負責檢查與維持資料一致性。

七、所有屬性都要用 Getter 與 Setter 嗎?

不需要。如果只是簡單的學生姓名,直接使用公開屬性就很合理:

class Student:
    def __init__(self, name):
        self.name = name

只有在需要驗證、轉換、唯讀控制或維持特殊規則時,才考慮 @property 或明確的操作方法。好的封裝不是讓程式變長,而是讓規則更明確。

八、No-AI → AI Pair → Challenge AI 實習

No-AI:先發現資料問題

class Product:
    def __init__(self, name, stock):
        self.name = name
        self.stock = stock

product = Product("水井文蛤", 100)
product.stock = -50

請思考:負數庫存有什麼問題?如果直接修改庫存,哪些規則可能被跳過?

AI Pair:與 AI 合作改善

建議提示詞:

請使用 Python 的 @property 改善 Product 類別,讓庫存不能小於零,並提供購買商品的方法。請比較公開屬性與 property 的優缺點,程式保持適合大學生理解。

拿到 AI 程式後,請測試:負數庫存、庫存不足、購買零件商品及正常購買。

Challenge AI:需求突然改變

新規則:「庫存不允許由外部直接設定,只能透過進貨 restock() 與銷售 purchase() 改變。」請設計唯讀的 stock 屬性,並判斷這樣是否比公開 Setter 更符合需求。

九、物件導向四個重要觀念比較

觀念核心問題智慧養殖範例
Class 類別如何組織資料與行為?建立 Pond
Inheritance 繼承如何共用與擴充既有設計?SmartPond(Pond)
Polymorphism 多型如何用相同方式操作不同物件?check_status()
Encapsulation 封裝如何管理資料存取並維持合理狀態?oxygen 的驗證

十、結語:AI 會寫封裝,但誰來決定規則?

AI 可以快速產生 @property、Getter 與 Setter,但只有理解應用情境的人,才能判斷哪些資料不合理、哪些操作應被允許,以及哪些規則需要由物件自己維護。

AI 協助寫程式;學生負責理解問題、設計規則、驗證行為。這是 AI 時代學習 Python 物件導向的重要能力。

Python 物件導向教學系列:(一)Class (二)Inheritance (三)Polymorphism (四)Encapsulation
下一篇建議:整合四大觀念,設計智慧養殖監控系統。