สัปดาห์ที่ 11 · Advanced · 🐰 Tamagotchi Edition

OOP + จัดระเบียบโค้ด

สัปดาห์นี้สอน OOP "ผ่านการเลี้ยง Pet ดิจิทัล" 🐰 — เพราะมันมี state (หิว/เบื่อ/อายุ) ที่เปลี่ยนได้ · มีหลาย instance (Mochi vs Tofu vs Mango) · และมี behavior (feed/play/sleep) — ตัวอย่างที่สอน OOP ได้สมบูรณ์แบบที่สุด · เริ่มเลย!

เป้าหมายสัปดาห์นี้

🔒 ทำไม "ตัดสินใจใช้ OOP หรือไม่ใช้" คือทักษะที่ AI ทำให้คุณไม่ได้ AI "เขียน class ได้" · แต่ AI ชอบ over-engineer — ขอเครื่องคำนวณ มันสร้าง 5 class · ขอ TODO list มันสร้าง abstract factory pattern · งานของคุณคือ "กด reject" เมื่อ AI ทำเกินที่ต้อง · ทักษะนี้ใช้ได้ทั้งชีวิต

🐰 Meet Mochi — Pet ตัวแรกของเรา

ก่อนเรียน "theory" ของ OOP · มารู้จัก Mochi ก่อน — Pet กระต่ายดิจิทัลของเรา · Mochi มี state ที่เปลี่ยนตามเวลา (ความหิว ความสุข อายุ) และเราสามารถดูแลด้วย method 3 อย่าง (feed/play/sleep)

🎮 ลองเลี้ยง Mochi เลย!

ลองรัน Pet class ด้านล่าง · แก้บรรทัด "ใช้งาน" เพื่อ feed/play/sleep หลาย ๆ ครั้ง · ดู state เปลี่ยน

class Pet: def __init__(self, name, species="🐰"): self.name = name self.species = species self.hunger = 5 # 0=อิ่ม · 10=หิวมาก self.happy = 5 # 0=เศร้า · 10=มีความสุข self.age_days = 0 def status(self): face = "😊" if self.happy >= 7 else "😐" if self.happy >= 4 else "😢" belly = "🍔" * max(0, 10 - self.hunger) return f"{self.species} {self.name} {face} หิว={self.hunger}/10 สุข={self.happy}/10 {belly} (อายุ {self.age_days} วัน)" def feed(self, food="แครอท"): self.hunger = max(0, self.hunger - 3) self.happy = min(10, self.happy + 1) print(f"😋 {self.name} กิน {food} → อิ่มขึ้น!") def play(self): self.happy = min(10, self.happy + 3) self.hunger = min(10, self.hunger + 1) print(f"🎾 {self.name} เล่นกับคุณ → สุขใจมาก!") def sleep(self): self.happy = min(10, self.happy + 2) self.age_days += 1 print(f"💤 {self.name} หลับ... อายุเพิ่มเป็น {self.age_days} วัน") # ใช้งาน — ลองเปลี่ยน / เพิ่มบรรทัดดู mochi = Pet("Mochi", "🐰") print(mochi.status()) mochi.feed("แครอท") mochi.play() mochi.sleep() print(mochi.status())

💡 ลองเพิ่ม mochi.play() 5 ครั้ง → ดูว่า hunger จะถึง 10 มั้ย (เล่นเยอะแล้วหิว!)

🤔 ทำไมไม่ใช้ dict?

ถ้าแค่ pet ตัวเดียว · ใช้ dict ก็ได้ — แต่พอมี 3 ตัว · code เริ่ม "ปวดหัว":

# ❌ Version 1: ใช้ dict — เริ่มมั่ว mochi = {"name": "Mochi", "hunger": 5, "happy": 5, "age": 0} tofu = {"name": "Tofu", "hunger": 5, "happy": 5, "age": 0} mango = {"name": "Mango", "hunger": 5, "happy": 5, "age": 0} def feed(pet, food): pet["hunger"] = max(0, pet["hunger"] - 3) print(f"{pet['name']} กิน {food}") def play(pet): pet["happy"] = min(10, pet["happy"] + 3) pet["hunger"] = min(10, pet["hunger"] + 1) print(f"{pet['name']} เล่น") # ปัญหา 1: ต้องส่ง pet เป็น parameter ทุกครั้ง feed(mochi, "แครอท") play(tofu) # ปัญหา 2: ถ้าพิมพ์ key ผิด — เงียบ ๆ ไม่มี error mochi["hungry"] = 0 # ❌ พิมพ์ผิด — แต่ Python ไม่ error print(mochi) # ตอนนี้ dict มี "hunger" AND "hungry" — งง! # ปัญหา 3: ถ้าเปลี่ยน field "hunger" → "hunger_level" # ต้องไปแก้ทุกที่ที่ใช้ — ลืม 1 ที่ = bug
OOP แก้ปัญหา 3 อย่าง
  1. ผูก data + function เข้าด้วยกัน · mochi.feed() > feed(mochi)
  2. field กำหนดใน __init__ ที่เดียว · ไม่ต้องจำว่ามี field อะไร
  3. เปลี่ยน field ใน 1 ที่ · IDE ช่วยเช็คทุกที่ที่ใช้

🧩 องค์ประกอบของ class — กายวิภาคของ Mochi

มาแยก code ของ Pet ออกเป็น "ชิ้นส่วน" ที่ตั้งชื่อให้รู้จัก:

class Pet:                              # ① class definition
    def __init__(self, name, species):  # ② constructor
        self.name = name                 # ③ attribute (instance data)
        self.species = species
        self.hunger = 5                  # ④ default value
        self.happy = 5

    def feed(self, food):                # ⑤ method
        self.hunger -= 3                  # ⑥ self = "ตัวกระต่ายตัวนี้"
        print(f"{self.name} กิน {food}")


mochi = Pet("Mochi", "🐰")               # ⑦ create instance
mochi.feed("แครอท")                      # ⑧ call method
print(mochi.hunger)                      # ⑨ access attribute
หมายเลขชื่อความหมายในแบบ "เลี้ยงสัตว์"
class Pet:"พิมพ์เขียว" ของ pet — บอกว่า pet ทุกตัวมีอะไร / ทำอะไรได้
__init__"พิธีต้อนรับน้องใหม่" — ทำงานครั้งเดียวตอนเกิด
self.name"แบบฟอร์มประจำตัว" ของ pet ตัวนี้
self.hunger = 5"ค่าเริ่มต้น" ตอนเกิด — ทุกตัวเริ่มที่ 5
def feed(self):"ความสามารถ" ของ pet — ทุกตัวมี method นี้
self"ตัวฉัน" — pet ตัวนี้ของฉัน · ไม่ใช่ตัวอื่น
Pet(...)"เลี้ยงตัวใหม่ขึ้นมา" — เกิด instance
mochi.feed(...)"ให้อาหารกระต่ายชื่อ Mochi"
mochi.hunger"ดูว่า Mochi หิวแค่ไหน"

📐 Class Diagram (Mermaid)

classDiagram class Pet { +string name +string species +int hunger +int happy +int age_days +status() string +feed(food) void +play() void +sleep() void } note for Pet "+ = public · ทุก attribute / method" note for Pet "🐰 Mochi · 🐱 Tofu · 🐶 Mango = แต่ละ instance"

🎯 หลาย Pet = หลาย State แยกกัน

"ทำไมไม่ใช้ global variable?" — เพราะ pet หลายตัวต้องมี state ของตัวเอง · class ให้คุณสร้าง หลาย instance โดยแต่ละตัวมี state ไม่ปะปนกัน

class Pet: def __init__(self, name, species): self.name = name self.species = species self.hunger = 5 self.happy = 5 def feed(self): self.hunger = max(0, self.hunger - 3) def status(self): return f"{self.species} {self.name}: หิว={self.hunger}, สุข={self.happy}" # สร้าง 3 ตัว — แต่ละตัวเริ่มที่ state เริ่มต้นเหมือนกัน mochi = Pet("Mochi", "🐰") tofu = Pet("Tofu", "🐱") mango = Pet("Mango", "🐶") print("=== เริ่มต้น ===") print(mochi.status()) print(tofu.status()) print(mango.status()) # feed แค่ Mochi 2 ครั้ง mochi.feed() mochi.feed() print("\n=== หลัง feed Mochi 2 ครั้ง ===") print(mochi.status()) # หิว 0 (อิ่มที่สุด!) print(tofu.status()) # หิวยัง 5 — ไม่ได้ถูก feed print(mango.status()) # หิวยัง 5 # self ทำให้ Python รู้ว่า "feed ตัวไหน"
class Pet:
    def __init__(self, name):
        self.name = name
        self.hp = 100

    def attack(self, other, dmg):
        other.hp -= dmg

a = Pet("Mochi")
b = Pet("Tofu")
a.attack(b, 30)
a.attack(b, 20)
print(a.hp, b.hp)
100 50
a.attack(b, 30) ลด b.hp เป็น 70 · a.attack(b, 20) ลดอีก เป็น 50 · a.hp ไม่เปลี่ยน — เพราะ method ทำกับ other (=b) · นี่คือพลังของ self ที่แยกระหว่าง 2 instance

✅ เมื่อไหร่ "ควรใช้" OOP

สัญญาณตัวอย่างใช้ class ดีกว่า function
มีหลาย instance ที่มี structure เหมือนกันpet หลายตัว · sensor หลายตัว · booking หลายรายการ
มี state ที่เก็บ + เปลี่ยนCart (เพิ่ม/ลบของ) · BankAccount (deposit/withdraw)
operation หลายอันที่ทำกับข้อมูลชุดเดียวplayer.attack(), player.move(), player.heal()
ต้องการ encapsulation — ซ่อน detailinternal counter, cache, lazy loading

🚫 เมื่อไหร่ "ไม่ควรใช้" OOP

สัญญาณตัวอย่างใช้ function ก็พอ
Script สั้น ๆ ที่ทำครั้งเดียว"แปลง CSV เป็น JSON ทีเดียว"
มี instance เดียวconfig app, global logger✅ ใช้ module + function
Data analysis pipelinegroupby/merge/plot✅ ใช้ pandas DataFrame ก็พอ
Utility ไม่มี statedef calc_grade(score) · def hash_id(s)

🤖 AI Over-Engineer Detector — ฉากตลก ๆ ที่เจอจริง

AI ชอบ "show off" ทุกอย่างที่เรียน — รวมทั้ง design patterns ที่ไม่ต้องใช้ · นี่คือฉากที่นักศึกษาเจอบ่อย:

user: "ขอ Python ที่บวก 2 เลข" # 🎯 What you wanted: def add(a, b): return a + b print(add(3, 5))
user: "ขอ Python ที่บวก 2 เลข" # 🎭 What AI gave: from abc import ABC, abstractmethod from typing import Protocol class Calculator(Protocol): def calculate(self, a: float, b: float) -> float: ... class AbstractOperation(ABC): @abstractmethod def execute(self, a, b): pass class AddOperation(AbstractOperation): def execute(self, a, b): return a + b class CalculatorEngine: def __init__(self, op: AbstractOperation): self.op = op def run(self, a, b): return self.op.execute(a, b) engine = CalculatorEngine(AddOperation()) print(engine.run(3, 5))
❌ ทำไม AI version ถึงไม่ดี
  • ใช้ ABC, Protocol, @abstractmethod — concepts ปี 4
  • 15 บรรทัดแทนที่จะใช้ 2 บรรทัด
  • ทุกอย่างคือ "flexibility ที่ไม่มีใครต้องการ" — YAGNI principle
  • ถ้าจริง ๆ แล้ว project ต้องการ flex — รอ "เมื่อต้องการครั้งที่ 2" ค่อยเพิ่ม

🚩 5 สัญญาณว่า AI Over-Engineer

สัญญาณตัวอย่างวิธี reject
1 ฟังก์ชัน → 3+ class"add" → CalculatorEngine + AddOp + AbstractOperation"เขียนเป็น function อันเดียวพอ"
import แปลก ๆabc, Protocol, metaclass"ไม่ใช้ abc · ใช้ basic Python เท่านั้น"
มี Factory / Builder / Strategy patternclass TodoFactory · class StoreBuilder"ลบ pattern ทั้งหมด · เขียน straightforward"
ทุก method แค่ wrap call ของ method อื่นclass A → call B → call C → return value เดิม"flatten · ลบ class ที่ไม่มี state"
เพิ่ม generic / typing เกินจำเป็นGeneric[T, U, V] ทั้งที่ใช้ int"ใช้ type ปกติ · ไม่ต้อง generic"
🪄 เคล็ดลับ — Prompt ที่ดีกว่า ขอ AI "เขียนเป็น function ก่อน · ถ้าจำเป็นค่อยใส่ class · ห้ามใช้ design pattern" → AI จะถ่อมตัวลงและให้ code ที่ใช้งานได้จริง

📁 แยก code เป็น module (file) — เลี้ยง Pet เริ่มใหญ่

ตอนนี้ Pet class โต — มี save/load, มี shop, มี battle · ถ้าใส่ทุกอย่างใน main.py → 500+ บรรทัด หา function ไม่เจอ · ทางออก: แยก concern เป็นไฟล์

pet_app/
├── main.py          ← entry point — รันจากที่นี่
├── pet.py           ← class Pet (data + behavior หลัก)
├── store.py         ← save_pets(), load_pets() — JSON I/O
├── shop.py          ← buy_food(), buy_toy() — ระบบเศรษฐกิจ
└── ui.py            ← show_menu(), print_status() — display

pet.py

class Pet:
    def __init__(self, name, species="🐰"):
        self.name = name
        self.species = species
        self.hunger = 5
        self.happy = 5

    def feed(self, food):
        self.hunger = max(0, self.hunger - 3)

    def status(self):
        return f"{self.species} {self.name}: หิว={self.hunger}"

store.py

import json
from pet import Pet

def save_pets(pets, filename="pets.json"):
    data = [{"name": p.name, "species": p.species,
             "hunger": p.hunger, "happy": p.happy} for p in pets]
    with open(filename, "w", encoding="utf-8") as f:
        json.dump(data, f, ensure_ascii=False, indent=2)

def load_pets(filename="pets.json"):
    with open(filename, encoding="utf-8") as f:
        data = json.load(f)
    pets = []
    for d in data:
        p = Pet(d["name"], d["species"])
        p.hunger = d["hunger"]
        p.happy = d["happy"]
        pets.append(p)
    return pets

main.py

from pet import Pet
from store import save_pets, load_pets

def main():
    mochi = Pet("Mochi", "🐰")
    tofu  = Pet("Tofu",  "🐱")
    mochi.feed("แครอท")

    save_pets([mochi, tofu])
    print("เซฟแล้ว!")

if __name__ == "__main__":
    main()
กฎ — แยกตาม "ทำอะไร" ไม่ใช่ "มีอะไร" "pet.py" = data + behavior หลัก · "store.py" = I/O · "ui.py" = display · ห้ามมี everything.py

🔄 Refactor — แตก 1 ไฟล์ใหญ่

เอา todo.py จาก W07 (TODO Manager) มา refactor:

todo_v2/
├── main.py          ← parse argv, call commands
├── todo.py          ← class Todo (id, text, done)
├── store.py         ← load_todos(), save_todos()
└── commands.py      ← add_command(), list_command(), done_command(), delete_command()

ขอ AI ช่วย refactor — แต่ ทีละไฟล์:

spec: refactor todo.py เดิม (paste code) ให้แตกเป็น 4 ไฟล์ตาม structure ด้านบน
ทำทีละไฟล์ — เริ่มที่ todo.py · ต้องเก็บ behavior เดิมไว้ทุกประการ
อย่าเพิ่ม feature ที่ไม่อยู่ในเวอร์ชันเดิม
กฎทอง — Refactor ≠ Add Feature Refactor คือเปลี่ยน "รูปแบบ" โดย behavior เหมือนเดิม · ถ้า AI เพิ่ม feature → reject

🐱 Bonus: Inheritance — Cat สืบทอดจาก Pet

"แล้วถ้า Cat กับ Dog ทำงานต่างกัน?" — Cat ชอบกินปลา · Dog ชอบเล่นเยอะกว่า · ใช้ inheritance ให้ Cat / Dog "สืบทอด" จาก Pet โดยเปลี่ยนแค่ส่วนที่ต่าง

class Pet: def __init__(self, name, species): self.name = name self.species = species self.hunger = 5 self.happy = 5 def feed(self, food): self.hunger = max(0, self.hunger - 3) print(f"😋 {self.name} กิน {food}") def sound(self): return "..." # default — Pet เฉย ๆ # Cat สืบทอดจาก Pet — เพิ่ม "ชอบปลา" class Cat(Pet): def __init__(self, name): super().__init__(name, "🐱") # เรียก __init__ ของ Pet def feed(self, food): if food == "ปลา": self.happy = min(10, self.happy + 5) # ปลา = ดีใจสุด print(f"😻 {self.name} อร่อยมาก!") super().feed(food) # เรียก feed ของ Pet ปกติ def sound(self): return "เมี้ยว~" # Dog สืบทอดจาก Pet — เพิ่ม "ชอบเล่นเยอะ" class Dog(Pet): def __init__(self, name): super().__init__(name, "🐶") self.energy = 10 # field ใหม่เฉพาะ Dog def fetch(self): if self.energy > 0: self.happy = min(10, self.happy + 4) self.energy -= 2 print(f"🎾 {self.name} วิ่งเอาลูกบอลมาให้!") else: print(f"😴 {self.name} เหนื่อยแล้ว · นอนก่อน") def sound(self): return "โฮ่ง โฮ่ง!" # ใช้งาน mochi = Cat("Mochi") mango = Dog("Mango") mochi.feed("ปลา") # Cat's special feed print(f" {mochi.name} พูดว่า: {mochi.sound()}") mango.fetch() # Dog-only method mango.fetch() print(f" {mango.name} พูดว่า: {mango.sound()}") print(f" energy: {mango.energy}/10") # Both ยังใช้ feed/property ของ Pet ได้ print(f"\n{mochi.name}: หิว={mochi.hunger}, สุข={mochi.happy}") print(f"{mango.name}: หิว={mango.hunger}, สุข={mango.happy}")
Inheritance ในแบบ "สายเลือดสัตว์"
  • Pet = ปู่ย่า · กำหนดสิ่งที่ทุกสัตว์มี (hunger, happy, feed)
  • Cat / Dog = ลูกหลาน · สืบทอดของเดิม + เพิ่ม/เปลี่ยนตามชนิด
  • super().feed() = "เรียกของพ่อแม่" — ทำของพื้นฐานก่อนค่อยเพิ่ม

📐 Mermaid — Inheritance Tree

classDiagram Pet <|-- Cat Pet <|-- Dog class Pet { +name : str +species : str +hunger : int +happy : int +feed(food) +sound() str } class Cat { +feed(food) [override] +sound() ["เมี้ยว~"] } class Dog { +energy : int +fetch() [new!] +sound() ["โฮ่ง!"] }
⚠️ ใช้ inheritance เมื่อ "is-a" จริง ๆ Cat "is a" Pet ✅ · Dog "is a" Pet ✅ · แต่ Cart "is a" Pet ❌ · ถ้าฝืน inherit ที่ไม่ใช่ is-a → code งงและพัง · ปลอดภัยกว่า: "composition" (Pet has-a Inventory)

🎨 Type hints — สำคัญในยุค AI

ใส่ type ใน function signature — AI อ่านง่ายขึ้น 5 เท่า · IDE auto-complete ฉลาดขึ้น · เครื่องมือเช็คได้

from typing import Optional class Pet: def __init__( self, name: str, species: str = "🐰", hunger: int = 5, owner: Optional[str] = None, ) -> None: self.name = name self.species = species self.hunger = hunger self.owner = owner def feed(self, food: str, amount: int = 1) -> int: """ให้อาหาร · คืนค่า hunger หลังกิน""" self.hunger = max(0, self.hunger - amount * 3) return self.hunger def is_hungry(self) -> bool: return self.hunger >= 7 # AI / IDE จะรู้ทันที ว่า: # - name ต้องเป็น str (ไม่ใช่ int) # - amount default = 1 # - feed() คืน int # - is_hungry() คืน bool mochi = Pet("Mochi", "🐰") new_hunger = mochi.feed("แครอท", amount=2) print(f"หลังกิน หิว = {new_hunger}") print(f"หิวมั้ย? {mochi.is_hungry()}")
กฎ — ทุก method สำคัญใส่ type hint โดยเฉพาะ __init__ · type hint = "contract" ที่บอกว่าใช้ class นี้ยังไง · AI ใช้ type hint เพื่อแก้ code ได้แม่นขึ้น

🧰 Standard structure ของ Python project

my_project/
├── README.md              ← อธิบายโปรเจกต์
├── requirements.txt       ← list ของ library ที่ต้องลง
├── .gitignore             ← บอก git ว่าไฟล์ไหนไม่เก็บ
├── .env                   ← secrets (ไม่ commit!)
├── main.py                ← entry point
├── src/                   ← code หลัก
│   ├── __init__.py
│   ├── student.py
│   └── grading.py
├── tests/                 ← unit tests (W13)
│   └── test_grading.py
└── data/                  ← ข้อมูล (มัก gitignore)
    └── students.csv

requirements.txt

pandas==2.1.0
numpy>=1.26
matplotlib>=3.8
requests

วิธีใช้: pip install -r requirements.txt

.gitignore

__pycache__/
*.pyc
.venv/
.env
*.csv
*.db

🌍 Virtual environment (.venv)

เก็บ library ของแต่ละ project ไว้ในห้องของตัวเอง · ไม่ปนกัน

# สร้าง .venv ใน project folder
python -m venv .venv

# Activate
# Windows:
.venv\Scripts\activate
# Mac/Linux:
source .venv/bin/activate

# ตอนนี้ pip install จะลงใน .venv เฉพาะ project นี้
pip install pandas

# ปิด
deactivate

🧪 Workshop — Refactor TODO Manager + ออกแบบ Pet ของตัวเอง

Part A: Refactor TODO Manager เป็น Module-based

  1. Copy todo.py จาก W07 มาวางใน folder ใหม่ todo_v2/
  2. วาด class diagram (Mermaid) — มีอะไรเป็น class · มีอะไรเป็น function อิสระ
  3. แตกเป็นไฟล์ ตาม structure ด้านบน · refactor ทีละไฟล์
  4. ใส่ type hints ทุก function/method
  5. เซ็ต virtual environment + requirements.txt
  6. ทดสอบ ว่าทุก command ที่เคยทำงานได้ใน v1 ยังทำงานได้ใน v2 — regression test (W05)
  7. commit + push GitHub — README อธิบายว่า "ทำไม refactor + structure ใหม่"

Part B: 🐰 Adopt Your Own Pet — ออกแบบ class จากศูนย์

เลือก 1 ตัว ที่ "คุณอยากเลี้ยง" · ออกแบบ class + เขียน + commit

ตัวเลือกState (attributes)Behaviors (methods)
🌱 Plant (เลี้ยงต้นไม้)name, height, water_level, sunlight, agewater(), give_sun(), trim(), grow()
🤖 Robot (Mechatronics)battery, position_x, position_y, payloadmove(), turn(), pickup(), recharge()
🎮 Player (RPG)name, hp, level, xp, inventoryattack(), heal(), level_up(), pick_up()
🚗 Car (Engineering sim)fuel, speed, position, gearaccelerate(), brake(), refuel(), shift()
Drink (สั่งกาแฟ)name, size, sweet_level, milk, priceadd_topping(), customize(), price()
📦 Order (ก๋วยเตี๋ยว)items, total, status, tableadd(), remove(), checkout(), receipt()
  1. เลือก 1 อัน · เขียน spec ใน Spec Scorecard — ระบุ attributes + methods + invariants (e.g. "battery 0-100")
  2. วาด class diagram (Mermaid) — ดูตัวอย่าง Pet ด้านบน
  3. เขียน class + รัน + ทดสอบ 5 scenarios — ใช้ Python Runner ด้านบนเป็น template
  4. สร้าง 3 instances · ทดสอบว่า state แยกกัน
  5. เพิ่ม subclass — เช่น Cactus extends Plant (น้ำน้อย) · CleanRobot extends Robot
  6. commit + push

ข้อผิดที่พบบ่อย

🤖➜🐰 ใส่ class ทุกที่ทั้งที่ function พอ AI ชอบเปลี่ยน def add(a, b) เป็น class Calculator · ถามตัวเอง: "จะมีหลาย instance ของสิ่งนี้มั้ย?" · ตอบไม่ = ไม่ต้องใช้ class · เป็น function ก็ "คนรัก AI ไม่ใช้ class เก่งกว่าคนที่ใส่ class ทุกที่"
🔥 ลืมใส่ self หน้า attribute def feed(self): hunger -= 3 ← ไม่มี self. = Python หา hunger ใน scope ของ method ไม่เจอ → NameError · ทุก attribute ต้อง self.hunger
🪢 Refactor + Add feature พร้อมกัน เปลี่ยน structure + เพิ่ม feature ทีเดียว → ถ้าพังไม่รู้ว่าจุดไหน · refactor → ทดสอบ → ค่อยเพิ่ม feature
🌀 Import แบบ wildcard (from x import *) ทำให้ไม่รู้ว่าชื่อมาจากไหน · IDE auto-complete ไม่รู้ · เพื่อนอ่าน code งง · ใช้ from x import name1, name2 เสมอ
🧬 Inheritance ผิดทาง — "ไม่ใช่ is-a" class Cart(Pet) — Cart ไม่ใช่ Pet · นี่คือ misuse inheritance · ตรวจ: "X is a Y?" — ถ้าตอบไม่ได้ ใช้ composition แทน

ส่งงานสัปดาห์นี้

Reference จาก slide เดิม

ครอบคลุม Topic 9 — OOP and Matplotlib ส่วน OOP + ขยายไปการจัด module + type hints + virtual env ตามมาตรฐานยุค AI