> ## Documentation Index
> Fetch the complete documentation index at: https://meta.niceshare.site/llms.txt
> Use this file to discover all available pages before exploring further.

# 開放封閉原則

> 開放封閉原則是指系統應對擴充開放、對修改封閉，用新程式碼增加行為，而不是改已經能工作的部分。了解邁耶起源、WordPress 外掛案例與邊界。

<Info>
  **類別**: 原則<br />
  **類型**: 系統設計原則<br />
  **來源**: 伯特蘭·邁耶，《物件導向軟體建構》，Prentice Hall，1988 年<br />
  **別名**: OCP；開閉原則；Open/Closed Principle
</Info>

<Note>
  **快速回答** — **開放封閉原則**（Open-Closed Principle）是指：已經能工作的模組應保持可被直接使用，新行為加在旁邊，而不是鑿進舊檔案裡。伯特蘭·邁耶在 1988 年為這個想法命名；羅伯特·C·馬丁後來把它說成「對擴充開放，對修改封閉」。實務檢驗很硬：新情況應落成新零件，而不是再去改一份已經能工作的程式碼。
</Note>

## 什麼是開放封閉原則？

開放封閉原則是一條設計規則：軟體實體——或任何結構相似的系統——應透過擴充接受新行為，而不是改寫客戶已經依賴的那些部分。

> 若一個模組仍可供擴充，就說它是開放的……若其他模組可以使用它，就說它是封閉的。

日常圖像是牆上的插座。你不會每買一盞燈就重佈一次家裡的電線。插座是封閉的：它的形狀是一份別人可以信任的契約。插座也是開放的：水壺、充電器或收音機都能插上去，而不必改牆。

在程式碼裡，這道鉸鏈是介面、鉤子或外掛槽。一長串 `if`，每來一種新稅、新支付方式或新學生類型就重開一次，形狀正好相反。[單一職責原則](/zh-hant/principles/single-responsibility)幫你把「一個變更理由」隔離出來。開放封閉原則接著問：把這個理由放在一扇穩定的門後面，好讓昨天的客戶不必搬家。

邁耶 1988 年的版本靠繼承：已編譯的類別對客戶封閉，後代再加功能。1990 年代，馬丁等人把同一道鉸鏈改寫成抽象介面和外掛。名字還在。機制從「去繼承舊模組」變成「依賴一份契約，再加一個新的實作者」。

### 開放封閉原則的三層理解

* **入門**：新情況來了，先問能不能加一塊，而不是去改舊的。如果每多一盞燈都要開牆，插座一開始就沒設計好。
* **實踐**：點名本季真正預期的變異——支付類型、報表格式、社團種類——在它周圍放一個穩定介面。然後把新情況做成新的處理程式。不要給叫不出名字的未來預留插槽；那場架屬於 [YAGNI](/zh-hant/principles/yagni-principle)。
* **進階**：你只能針對*已經預見到的*變異把模組封閉起來。鉸鏈猜錯了，會得到一套每次變更都還得拜訪的空框架。完全不設鉸鏈，每次變更又變成散彈槍式修改。這條原則是在賭下一個差異會落在哪裡，不是發誓永遠不碰檔案。

## 起源

開放封閉原則起於模組化難題，不是貼在團隊牆上的口號。

**伯特蘭·邁耶**當時在做 Eiffel 語言與方法，於 **1988 年**由 **Prentice Hall** 出版的《物件導向軟體建構》（*Object-Oriented Software Construction*）裡寫下它。第一版第 23 頁要求「令人滿意的模組分解」必須給出*既開放又封閉*的模組。開放意味著還能加欄位或功能。封閉意味著其他模組能從一份穩定介面使用它——編譯好、放進函式庫、作為基準線發布。他當時的技術答案是繼承：一個類別可以對客戶凍結，同時仍由後代擴充，而不打擾原件。

大家常引的那句短話並不是邁耶的原文。**1996 年 1 月**，《C++ Report》上 **羅伯特·C·馬丁**把他轉述為：軟體實體「應對擴充開放，對修改封閉」。這篇文章後來進入他 2000 年的 “Design Principles and Design Patterns”，以及 **2003 年** Prentice Hall 的《敏捷軟體開發：原則、模式與實務》。鉸鏈從實作繼承挪到抽象介面。新行為是滿足契約的新類別。舊原始碼不動。

底下還有兩條更老的想法。**1972 年 12 月**，**David Parnas** 在 *Communications of the ACM*（**15** 卷第 12 期：1053–1058）主張：按最可能變化的設計決策來分解系統——資訊隱藏，而不只是把資料包起來。**1996 年**，**Alistair Cockburn** 寫出 *Protected Variations*（受保護的變異）：找出預見的變異，在周圍放穩定介面。**Craig Larman** 在 *IEEE Software* **18**(3): 89–91（**2001 年 5/6 月**）裡，把開放封閉、受保護變異和帕納斯的隱藏當成同一條原則的不同名字。

大約 **2004 年**，**Michael Feathers** 把馬丁的「前五條」排成 SOLID 口訣。開放封閉原則是其中的 「O」。包裝讓名字出了名。機制並沒有被凍結。**2014 年 5 月 12 日**，馬丁寫道：外掛系統——Eclipse、Vim、Minecraft——是這條原則的「最終完成」：核心不指向外掛，外掛指向核心。

> 軟體實體（類別、模組、函數等）應對擴充開放，對修改封閉。

## 核心要點

開放封閉原則真正值錢的時候，是下一個差異很可能出現，而且你能在它到來之前叫出這種差異的*種類*。它不是禁止編輯。它是一條關於編輯應落在何處的規則。

<Steps>
  <Step title="封閉，是讓客戶能信任一份契約">
    模組封閉，意味著別人可以拿去用，而不必盯著你改寫。公開的形狀——介面、表格、插座——足夠靜止，他們的工作才不會跟著碎。如果每個新請求都逼你重開同一個函數，那些客戶綁的是你的私有決定，不是契約。封閉是為了*他們*，不是作者的獎盃。
  </Step>

  <Step title="開放，是讓新行為作為新零件到來">
    擴充是加程式碼、外掛、處理程式或子類別——不是在昨天的邏輯裡再插一條分支。商店用新轉接器接入一種支付方式，是開放。每來一個卡品牌就重開結帳檔案，不是。舊零件繼續工作，因為你沒有去鑿它。
  </Step>

  <Step title="鉸鏈是一個預見到的變異點">
    你封不住未知。你封的是能叫出名字的差異：圖形怎麼畫、稅怎麼算、社團怎麼辦。把一扇穩定的門放在那裡。[關注點分離](/zh-hant/principles/separation-of-concerns)告訴你哪些決策該分開。開放封閉原則告訴你其中哪些決策該*可插拔*。叫不出變異，就等。過早的鉸鏈，正是違反 [YAGNI](/zh-hant/principles/yagni-principle) 的方式。
  </Step>

  <Step title="不要把封住的檔案當成封住的缺陷">
    「對修改封閉」不是發誓永不修補漏洞、安全問題或錯誤抽象。不得為客戶改變*行為*的模組，仍可以接受修復。若契約本身錯了，你就改契約，並承擔代價。設計已經爛了還裝檔案神聖，是裝神弄鬼，不是原則。
  </Step>
</Steps>

## 應用場景

當一類情況會繼續增長、而舊情況還應繼續工作時，再用開放封閉原則。還沒理解的設計，不要用它來凍結。

<CardGroup cols={2}>
  <Card title="加一種新作業類型，而不是一團新亂麻">
    助教每來一份新作業就去改那份 400 行的評分腳本，等於每次開牆。點名共同契約——輸入、評分標準、輸出——把每次作業做成小處理程式。舊腳本保持封閉。新的一週是新檔案。[KISS](/zh-hant/principles/kiss-principle) 仍適用：契約要短。
  </Card>

  <Card title="接入一種支付或稅務規則">
    結帳、薪資和稅務引擎會在「每種新方法再加一個 `if`」裡腐爛。發布一個轉接器介面。把 Apple Pay、地區增值稅或學生折扣做成新的實作者。單獨測新零件。不要因為動了舊分支，就重測整台收銀機。
  </Card>

  <Card title="寫一份新社團能加入的章程">
    學校或社區團體常常每出現一個機器人社或合唱團，就重寫一遍章程。寫下*辦社規則*——誰能發起、錢怎麼報、何時活動——把社團名單放在附錄。章程封閉。成員開放。家庭值日表也一樣：格子形狀固定，名字可以換。
  </Card>

  <Card title="讓公共表格穩住，讓附表去長">
    報稅、申請資助、建築許可在真正好用時已經是這樣：短的核心表，再加上特殊情況的附表。加一張新附表，不該重寫第一頁。如果每個例外都逼整本小冊子再版，市民和辦事員都在為缺失的鉸鏈付錢。
  </Card>
</CardGroup>

## 經典案例

開放封閉原則作為操作選擇的有數字窗口，是 **WordPress** 及其外掛架構——不是聲稱部落格軟體變成了邁耶式的完美。

2004 年以前，人們用「駭客補丁」擴充 WordPress 及其前身 **b2**：一份說明書寫著*改這個核心檔案，貼這段*。升級會覆蓋貼上。定製和產品互相打架。**2004 年 5 月 22 日**，**Matt Mullenweg** 宣布 WordPress **1.2**，代號 “Mingus”。功能之一是「新的外掛架構」，讓外掛可以「鉤進 WordPress 幾乎每一個動作」。這些鉤子——action 與 filter——就是擴充點。新行為住在 `wp-content/plugins/`，不在升級會替換的那些檔案裡。

設計也改了功能歸誰。把外掛系統推進專案的 **Ryan Boren** 後來描述核心團隊的經驗規則：若功能對大約八成使用者沒有用，就試試做成外掛。隨 1.2 附帶的第一個例子是 **Hello Dolly**：一個列印歌詞的小外掛，坐在核心旁邊而不是裡面。機制就是不叫這個名字的開放封閉原則。核心給出契約。新行為是新零件。鉤子本身穩定時，舊站點升級仍可保住外掛。

後來的規模是公開的，而且是目錄計數，不是品質稽核。截至 **2026 年 8 月**，WordPress.org 外掛目錄自稱「超過 **71,000** 個免費外掛」。這個數字量的是槽有多寬。它不證明每個外掛都安全，也不證明核心本身永不改。邊界說明：WordPress 仍會為漏洞、安全和編輯器而改核心。鉤子一挪，許多外掛仍會在大版本上斷裂。還讓你去改核心的外掛，只是換了資料夾的舊補丁。案例展示的是鉸鏈——穩定的鉤子、新的檔案——不是聲稱 71,000 個附加元件的生態自動就設計良好。

## 邊界與失效場景

還叫不出變異時，開放封閉原則會失效。為從未到來的未來搭「靈活框架」，是典型的過度封閉：很多空插座，一盞真燈，房子卻沒人能重佈線。所以它必須挨著 [YAGNI](/zh-hant/principles/yagni-principle) 和[迭代改進](/zh-hant/principles/iterate-and-improve)。在第二、第三個相似情況之後再關鉸鏈，不要在第一個之前。

它也會在必須修改並重新認證的製品上失效。醫療器械、航空程序、法院已經解釋過的法律表格，不能靠非正式外掛生長。那裡的「封閉」是監管，擴充仍須經過新的批准版本。把禁止的分叉叫成「對擴充開放」，是類別錯誤。

常見誤用是把繼承當反射。邁耶 1988 年的答案是子類別。馬丁後來的答案是外掛和抽象介面。共享了錯誤父類別的深繼承樹，不是開放封閉設計，是你以後還得重開的層級。另一種誤用是把 [DRY](/zh-hant/principles/dry-principle) 理解成「一行都不要複製，永遠加策略物件」。五六行案例的重複，有時比過早的鉸鏈更便宜。

## 常見誤區

這個中文名容易撞上「永遠別改舊程式碼」「必須用繼承」，以及把 SOLID 當成給類別做性格測驗。

<AccordionGroup>
  <Accordion title="對修改封閉，就是永遠不改現有檔案">
    漏洞、安全問題和錯誤契約仍要修。封閉是相對於*某一行為的客戶*：你加了一個他們不用的情況，他們不該被迫搬家。若公開契約本身錯了，你就改它，並付漣漪的帳。不能打補丁的檔案不是封閉，是被遺棄。
  </Accordion>

  <Accordion title="開放封閉原則就是繼承，或只屬於物件導向程式碼">
    邁耶第一版用繼承，因為他當時在教這件工具。外掛鉤子、函數表、設定，甚至紙本附錄，形狀都一樣：穩定的門，旁邊的新零件。馬丁 2014 年的要點是，外掛架構是這條原則的完整尺寸。牆上的插座不是子類別。
  </Accordion>

  <Accordion title="每個類別在第一天就該開放封閉">
    你沒見過的變異，封不住。投機的策略物件、空的外掛 API、「以防萬一」的框架，是團隊被插座淹死的方式。等第二個真實情況。再抽出鉸鏈。在那之前，清楚的 `if` 是誠實的。原則是對預見變化的回應，不是對每個新檔案收稅。
  </Accordion>
</AccordionGroup>

## 相關概念

這些頁面挨著同一個問題：怎樣改變系統，又不搖晃已經能工作的一切。

<CardGroup cols={3}>
  <Card title="單一職責原則" href="/zh-hant/principles/single-responsibility">每個模組一個變更理由。開放封閉原則決定這個理由*允許落在哪裡*。</Card>
  <Card title="關注點分離" href="/zh-hant/principles/separation-of-concerns">把不同決策分開。可插拔需要先有乾淨的切口，才需要外掛槽。</Card>
  <Card title="YAGNI 原則" href="/zh-hant/principles/yagni-principle">變異成真之前，不要去建鉸鏈。沒有 YAGNI 的開放封閉會變成投機架構。</Card>
  <Card title="DRY 原則" href="/zh-hant/principles/dry-principle">一個事實一份表述。過度 DRY 會逼出假鉸鏈；不足 DRY 則反覆重開同一檔案。</Card>
  <Card title="KISS 原則" href="/zh-hant/principles/kiss-principle">讓契約保持短。臃腫的外掛 API 並不比幾條誠實的分支更簡單。</Card>
  <Card title="迪米特法則" href="/zh-hant/principles/law-of-demeter">只跟直接鄰居說話。把內部洩漏出去的封閉模組，並不封閉。</Card>
</CardGroup>

## 一句話總結

<Tip>
  下一個情況若是你已經叫得出的種類，就在穩定的門後面加一塊新零件——不要為每一盞燈重開牆。
</Tip>
