跳轉至

建立 2026-11-12 更新 2026-11-12 HL only

B3.2 多個類別的 OOP(HL)

多個類別的物件導向程式設計(Fundamentals of OOP for multiple classes)說明類別之間如何建立關係:用繼承重用程式碼、用多型讓同一個方法呼叫有不同行為、用抽象類別定義共同介面,以及用組合與聚合把物件組合成更大的系統。本子題只有 HL 需要學,最後也介紹幾個常見的設計模式。

HL 專屬子題

B3.2 整個子題只出現在 HL 考卷,SL 學生可略過本頁。

繼承如何重用程式碼?

繼承(inheritance) 讓 子類別(subclass) 自動擁有 父類別(superclass) 的屬性與方法,再加入或修改自己的部分。表示「is-a」關係:學生 是一種 人。

class Person:
    def __init__(self, name):
        self._name = name            # protected

    def introduce(self):
        return f"I am {self._name}."


class Student(Person):
    def __init__(self, name, level):
        super().__init__(name)       # 呼叫父類別建構子
        self._level = level

    def introduce(self):             # 覆寫(override)
        return f"I am {self._name}, an IB CS {self._level} student."


class Teacher(Person):
    def __init__(self, name, subject):
        super().__init__(name)
        self._subject = subject

    def introduce(self):
        return f"I am {self._name} and I teach {self._subject}."
public class Person {
    protected String name;
    public Person(String name) { this.name = name; }
    public String introduce() { return "I am " + name + "."; }
}

public class Student extends Person {
    private String level;
    public Student(String name, String level) {
        super(name);
        this.level = level;
    }
    @Override
    public String introduce() {
        return "I am " + name + ", an IB CS " + level + " student.";
    }
}

多型是什麼?

多型(polymorphism) 指同一個方法呼叫,依物件實際的類別執行不同的實作:

people = [Student("Amy", "HL"), Teacher("Mr. Lee", "CS"), Person("Guest")]
for p in people:
    print(p.introduce())     # 各自呼叫自己類別的 introduce()
  • 覆寫(overriding):子類別重新定義父類別的同名方法,屬於執行期多型。
  • 多載(overloading):同一個類別中,同名方法有不同參數列,例如 Java 的 add(int a, int b) 與 add(double a, double b)。Python 不直接支援多載,通常用預設參數取代。

多型的好處是:新增一種 Person 子類別時,上面的迴圈完全不用改。

抽象類別與介面

抽象類別(abstract class) 不能直接建立物件,只用來定義子類別必須實作的方法,確保所有子類別都有共同介面。Java 另外有 介面(interface),一個類別可以實作多個介面。

from abc import ABC, abstractmethod
import math

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

class Circle(Shape):
    def __init__(self, r):
        self.r = r
    def area(self):
        return math.pi * self.r ** 2

class Rectangle(Shape):
    def __init__(self, w, h):
        self.w, self.h = w, h
    def area(self):
        return self.w * self.h
public abstract class Shape {
    public abstract double area();
}

public class Circle extends Shape {
    private double r;
    public Circle(double r) { this.r = r; }
    public double area() { return Math.PI * r * r; }
}

組合與聚合有什麼不同?

兩者都是「has-a」關係,差別在 生命週期:

  • 組合(composition):部分不能脫離整體而存在。房子被拆了,房間也不存在。
  • 聚合(aggregation):部分可以獨立存在。班級解散了,學生仍然存在。
class Course:
    def __init__(self, title):
        self.title = title
        self.students = []                # 聚合:學生物件由外部建立、可獨立存在
        self.syllabus = Syllabus(title)   # 組合:課綱隨課程建立與消滅

    def enrol(self, student):
        self.students.append(student)

優先使用組合而非繼承

繼承讓子類別和父類別緊密綁在一起,父類別一改,所有子類別都受影響。若關係是「has-a」而不是「is-a」,用組合會更有彈性。

UML 類別圖中的關係

classDiagram
    Person <|-- Student
    Person <|-- Teacher
    Course o-- Student : 聚合
    Course *-- Syllabus : 組合
    Teacher --> Course : 教授
    class Person {
        #String name
        +introduce() String
    }
    class Student {
        -String level
        +introduce() String
    }
    class Teacher {
        -String subject
        +introduce() String
    }
    class Course {
        -String title
        +enrol(Student) void
    }
    class Syllabus

空心三角形箭頭表示繼承、空心菱形表示聚合、實心菱形表示組合,# 表示 protected。

常見設計模式

設計模式(design pattern) 是常見設計問題的經典解法:

模式 解決的問題 例子
單例(Singleton) 整個程式只能有一個實例 資料庫連線管理、設定檔
工廠(Factory) 依條件建立不同子類別物件,呼叫者不必知道細節 ShapeFactory.create("circle")
觀察者(Observer) 一個物件狀態改變時,自動通知多個訂閱者 成績公布時通知學生與家長 App

推薦影音

Python OOP:繼承與子類別(Corey Schafer)

簡述:示範如何用 super() 呼叫父類別建構子、覆寫方法,以及 isinstance()、issubclass() 的用法,對應本頁繼承一節。

Java 程式設計入門(freeCodeCamp)

簡述:freeCodeCamp 的 Java 入門課程後半段講解類別、物件、繼承與多型,適合學校使用 Java 的 HL 學生搭配本頁練習。

練習題

練習 1:is-a 或 has-a

判斷下列關係應用繼承還是組合/聚合:(a) Car 與 Engine;(b) ElectricCar 與 Car;(c) Library 與 Book。

參考答案

(a) 組合,汽車「有」引擎;(b) 繼承,電動車「是一種」汽車;(c) 聚合,圖書館「有」書,書可以獨立存在(被移到另一間圖書館)。

練習 2:多型

建立抽象類別 Employee,有抽象方法 monthly_pay()。子類別 FullTime(月薪固定)與 PartTime(時薪 × 時數)。寫一段程式計算所有員工的薪資總和。

參考答案
from abc import ABC, abstractmethod

class Employee(ABC):
    def __init__(self, name):
        self.name = name
    @abstractmethod
    def monthly_pay(self):
        pass

class FullTime(Employee):
    def __init__(self, name, salary):
        super().__init__(name)
        self.salary = salary
    def monthly_pay(self):
        return self.salary

class PartTime(Employee):
    def __init__(self, name, rate, hours):
        super().__init__(name)
        self.rate, self.hours = rate, hours
    def monthly_pay(self):
        return self.rate * self.hours

staff = [FullTime("Amy", 50000), PartTime("Ben", 200, 60)]
print(sum(e.monthly_pay() for e in staff))   # 62000
練習 3:覆寫與多載

說明覆寫(overriding)與多載(overloading)的差別,各舉一例。

參考答案

覆寫:子類別重新定義父類別的同名同參數方法,例如 Student.introduce() 覆寫 Person.introduce(),執行時依物件實際類別決定呼叫哪個。多載:同一類別中同名但參數不同的方法,例如 Java 的 area(double r) 與 area(double w, double h),編譯時依參數決定。

練習 4:設計模式

一個學校 App 需要在「成績公布」時,同時推播通知給學生、家長與導師。應使用哪個設計模式?簡述其結構。

參考答案

觀察者模式(Observer)。GradeBook(被觀察者)維護一份訂閱者清單,提供 subscribe() 與 notify();學生、家長、導師各自實作 update() 方法。成績公布時 GradeBook 呼叫 notify(),逐一呼叫每個訂閱者的 update()。新增通知對象時不需要修改 GradeBook。