一、物件導向(OOP)到底在解決什麼?
一句話版本:
把「資料 + 行為」包在一起,讓程式像真實世界一樣被理解與維護
沒用 OOP 時(程序式)
user_name = "Tom"問題:
user_age = 20
def say_hi(name):
print(f"Hi {name}")
say_hi(user_name)
- 資料跟行為是分散的
- user 一多,你會開始複製貼上地獄 🔥
用 OOP 的思維
class User:
def __init__(self, name, age):
self.name = name
self.age = age
def say_hi(self):
print(f"Hi {self.name}")
u1 = User("Tom", 20)
u1.say_hi()
👉 User 是一個「概念」
👉 name / age 是它的「狀態」
👉 say_hi() 是它「能做的事」
二、OOP 四大核心概念(超重要)
1️⃣ 封裝(Encapsulation)
外部不該知道的,就不要讓它亂改
class BankAccount:
def __init__(self, balance):
self._balance = balance # 慣例:_ 表示內部用
def deposit(self, amount):
self._balance += amount
def get_balance(self):
return self._balance
好處:
- 以後要加「不能負數」「記 log」都只改這個 class
2️⃣ 繼承(Inheritance)
共通行為抽到父類,子類只管差異
class Animal:
def speak(self):
pass
class Dog(Animal):
def speak(self):
print("汪")
class Cat(Animal):
def speak(self):
print("喵")
⚠️ 大型專案重點
繼承不要亂用,寧願組合(composition)
3️⃣ 多型(Polymorphism)
animals = [Dog(), Cat()]
for a in animals:
a.speak()
👉 呼叫一樣的方法
👉 行為卻不一樣 👉 UI / Pipeline / Plugin 系統的靈魂
4️⃣ 抽象(Abstraction)
用在「規範介面」,不是寫實作
from abc import ABC, abstractmethod
class DataLoader(ABC):
@abstractmethod
def load(self):
pass
class ImageLoader(DataLoader):
def load(self):
print("load images")
👉 很適合:
- Plug-in
- Dash / PyQt / pipeline 架構
(你之前做的 Dash + PyQt 其實超適合這套)
三、真的在「大型專案」時,怎麼設計?
❌ 新手最常犯的錯
- 一個 class 1000 行
- UI 直接寫資料處理
- 到處
import * - 沒有邊界,全部互相知道彼此
四、大型專案的核心原則(必背)
🧠 1️⃣ 單一職責(SRP)
一個 class 只負責一件事
❌
class Analyzer:
def load_data(self): ...
def analyze(self): ...
def draw_plot(self): ...
def save_excel(self): ...
✅
class DataLoader: ...
class Analyzer: ...
class Plotter: ...
class ReportWriter: ...
🧱 2️⃣ 分層架構(超重要)
常見 4 層
UI 層 (PyQt / Dash)
↓
Service 層 (流程、商業邏輯)
↓
Domain 層 (核心物件、規則)
↓
Infrastructure 層 (檔案、DB、API)
👉 UI 不碰資料細節
👉 資料怎麼來,換方式也不影響上層
🗂️ 3️⃣ 專案目錄結構範例
project/
│
├── ui/
│ └── main_window.py
│
├── services/
│ └── analysis_service.py
│
├── domain/
│ ├── image.py
│ ├── defect.py
│ └── result.py
│
├── infra/
│ ├── file_loader.py
│ └── logger.py
│
└── main.py
五、真實專案示例(接近你在做的事)
# domain/result.py
class AnalysisResult:
def __init__(self, defects):
self.defects = defects
# services/analysis_service.py
class AnalysisService:
def __init__(self, loader, analyzer):
self.loader = loader
self.analyzer = analyzer
def run(self, folder):
data = self.loader.load(folder)
return self.analyzer.analyze(data)
👉 這樣你之後
- 換資料來源
- 換分析算法
- 換 UI
都不用重寫整個世界 🌍
六、我給你的「大型專案心法」
🔑 設計前先問這 5 個問題
- 這個 class 在現實世界是什麼?
- 它「負責」什麼,不負責什麼?
- 它需要知道誰?
- 如果明天需求改了,哪裡最容易爆?
- 這東西能不能被替換?








