Notes & software courses · Free to learn
Aph's Blog
On this page

Defensive Programming

👋 อ่านฟรีทั้งหมดบน Aph's Blog — เนื้อหาภาษาไทย ทำตามทีละหน้าใน sidebar ได้เลย หากมีข้อเสนอแนะหรืออยากให้เพิ่มหัวข้อไหน บอกได้เสมอ

ตรวจสอบ input ที่ขอบระบบและเขียนโค้ดที่ "พังเร็ว พังชัด" แทนที่จะคำนวณผิดเงียบ ๆ

defensive programming คือการคิดล่วงหน้าว่า "อะไรพังได้บ้าง" แล้วป้องกันไว้ที่จุดที่ข้อมูลเข้าระบบ หลักการคือ fail fast — ถ้าข้อมูลผิดให้หยุดทันทีที่จุดเกิดเหตุ ดีกว่าปล่อยให้ค่าผิดไหลไปพังที่อื่นซึ่งหายากกว่า

ตรวจ input ที่ขอบระบบ (validate)

ข้อมูลจากภายนอก (ผู้ใช้, ไฟล์, API) เชื่อถือไม่ได้ ให้ตรวจที่จุดรับเข้าก่อนนำไปใช้ — ภายในระบบที่ตรวจแล้วค่อยไว้ใจได้

python
def create_account(name: str, age: int):
    if not name or not name.strip():
        raise ValueError("ชื่อต้องไม่ว่าง")
    if not isinstance(age, int):
        raise TypeError("อายุต้องเป็นจำนวนเต็ม")
    if not 0 < age < 150:
        raise ValueError(f"อายุไม่สมเหตุสมผล: {age}")
    return {"name": name.strip(), "age": age}

create_account("", 25)    # ValueError: ชื่อต้องไม่ว่าง (พังทันทีตรงจุด)

EAFP vs LBYL — สองสไตล์การตรวจ

มี 2 แนวคิด: LBYL (เช็คก่อนทำ) และ EAFP (ลองทำเลย แล้วจับ error) — Python นิยม EAFP เพราะอ่านง่ายกว่าและเลี่ยง race condition

python
data = {"name": "Aph"}

# LBYL — Look Before You Leap (เช็คก่อน)
if "age" in data:
    age = data["age"]
else:
    age = 0

# EAFP — Easier to Ask Forgiveness than Permission (ลองเลย จับทีหลัง)
try:
    age = data["age"]
except KeyError:
    age = 0

# จริง ๆ เคสนี้ Pythonic สุดคือ:
age = data.get("age", 0)
สไตล์แนวคิดเหมาะกับ
LBYLเช็คเงื่อนไขก่อนลงมือเงื่อนไขชัดเจน ไม่ซับซ้อน
EAFPลองทำแล้วจับ errorงาน I/O, dict/attribute access (Pythonic)

assert — ตรวจสมมติฐานระหว่างพัฒนา

assert ใช้ตรวจสิ่งที่ "ควรจะเป็นจริงเสมอ" ในโค้ด ถ้าไม่จริงจะ raise AssertionError ทันที เหมาะตรวจ logic ภายในระหว่างพัฒนา

python
def apply_discount(price, percent):
    assert 0 <= percent <= 100, f"เปอร์เซ็นต์ผิด: {percent}"
    return price * (1 - percent / 100)

apply_discount(100, 150)   # AssertionError: เปอร์เซ็นต์ผิด: 150
อย่าใช้ assert ตรวจ input ผู้ใช้

assert ถูก "ปิด" ทั้งหมดเมื่อรันด้วย python -O (optimized mode) ดังนั้นถ้าใช้ assert ตรวจ input จากผู้ใช้ พอรันโหมด -O การตรวจจะหายไปและข้อมูลผิดจะหลุดเข้าระบบ — ใช้ assert กับ "สมมติฐานภายในของโปรแกรมเมอร์" เท่านั้น ส่วน input ผู้ใช้ให้ใช้ if + raise

สรุปหัวข้อนี้

  • validate ข้อมูลจากภายนอกที่ขอบระบบ — fail fast พังตรงจุดเกิดเหตุ
  • LBYL เช็คก่อนทำ, EAFP ลองแล้วจับ — Python นิยม EAFP
  • assert ตรวจสมมติฐานภายในระหว่างพัฒนา
  • ห้ามใช้ assert ตรวจ input ผู้ใช้ (ถูกปิดด้วย python -O) — ใช้ if + raise
แบบฝึกหัด

1) เขียนฟังก์ชัน register(email, password) ที่ validate: email มี @, password ยาว >= 8 2) เขียนการเข้าถึง dict ทั้งแบบ LBYL และ EAFP แล้วเทียบ 3) ใช้ assert ตรวจว่าผลลัพธ์ภายในฟังก์ชันไม่ติดลบ 4) อธิบายว่าทำไมไม่ควรใช้ assert ตรวจ input ผู้ใช้