iT邦幫忙

2026 iThome 鐵人賽

DAY 20
0

抽象(Abstraction)是保留與目前目的有關的重要特徵,省略不需要關注的細節。

抽象分支保留呼叫端需要的共同操作,將具體做法交給實作。新舊實作可以同時保留在程式中,再依需要切換。這就是「分支」,讓團隊能在維持原本功能的同時,逐步開發與驗證新實作。

寄信操作與實作

在 Nathan 團隊透過依賴注入切換寄信實作的案例中,CustomerNotifier 把收件地址與準備好的信件交給 Sender,由寄信實作決定要立即寄送還是排入佇列。

換了寄信實作,CustomerNotifier 仍然呼叫同一個 send()。這裡的抽象,就是保留共同的寄信操作,讓通知功能不必知道每種寄送方式怎麼做。

PHP 的 interface 是表達這個抽象的方式,宣告各個實作都要提供的操作。設計抽象不只是定義方法宣告而已,還要定義操作的行為,包括需要哪些資料,以及呼叫後可以預期什麼結果。

新增每日摘要需求

Nathan 團隊又接到一個新需求。原本客戶追蹤的協作文件每次更新,網站就會寄出一封通知。有些客戶不需要即時收到每次更新,希望改成每天收到一封摘要,列出當天更新的文件與內容。這封摘要會在每天結束後寄出。

前面的改版只調整寄送方式,通知功能仍然照原本的方式準備信件,再交給 Sender 寄送。現在要把多筆文件更新整理成一封摘要,就需要先收集哪份文件在什麼時間更新,以及要通知哪位客戶,再準備信件。但 Sender 接收的是準備好的信件,只切換寄信實作,無法改變前面準備信件的方式。

建立通知流程介面

原本呼叫 CustomerNotifier::notify() 時,只傳入收件地址與文字。這次可以建立通知流程介面,讓新的 notify() 接收客戶 ID、收件地址、文件名稱、更新時間與更新內容。

接著建立逐筆通知實作與每日摘要實作。逐筆通知實作每收到一筆文件更新,就準備信件寄送;每日摘要實作則先保存資料,每天結束後再依客戶彙整成一封信寄送。

下圖由上到下呈現呼叫端、通知流程介面、兩種通知實作與寄信介面 Sender 的關係:

https://ithelp.ithome.com.tw/upload/images/20261004/20102562R6UkE3cJ9T.png

兩種通知實作都透過 Sender 寄信,因此可以共用原本的寄信程式。程式會依客戶選擇的通知方式,使用逐筆通知實作或每日摘要實作。

兩種通知實作的整合方式,以及驗證與切換做法,可以參考前面的說明。

依需求選擇抽象層級

前面的需求只改變寄送方式,這次則連準備信件的方式也要改變。兩個需求要調整的程式範圍不同,因此選擇的抽象層級也不同。從呼叫端提供的資料與實作負責的事情,可以看出兩者的差別:

抽象 呼叫端提供的資料 實作負責的事
寄信操作 收件地址與準備好的信件。 立即寄送,或依網域排入佇列。
通知流程 客戶 ID、收件地址、文件名稱、更新時間與內容。 逐筆準備信件並寄送,或先保存資料、每天結束後彙整成摘要並寄送。

Sender 的抽象只處理寄信,通知流程的抽象則包含準備信件與寄信。這次需求連準備信件的方式都要改變,因此需要在通知流程這一層提供不同實作。

如果只改寄送方式,原本準備信件的流程可以繼續使用,就不必另外建立兩套通知流程,增加選擇流程的程式與相關測試。

小結

要抽象哪些操作,取決於需求改變了哪些行為。先理解業務需求,才能決定呼叫端需要知道什麼,以及哪些做法可以交給不同實作。

參考資料


上一篇
Day 19:透過抽象分支驗證與切換新實作
系列文
重新認識主幹開發(Trunk-Based Development) 共 20 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言