當建置複雜的軟體系統時,僅寫出「可運作」的程式碼是不夠的。隨著系統規模擴大與需求演進,若物件建立方式結構不良、耦合過緊、控制流程寫死,以及靜態繼承層級僵化,都會導致架構脆弱不堪。
本指南完整拆解五大必備設計模式——抽象工廠、策略、轉接器、觀察者與裝飾器——並搭配真實企業情境、反模式、權衡取捨,以及逐步重構範例。
📊 軟體設計模式速查表
| 模式 | 類別 | 核心意圖 | 真實世界情境 | 主要優勢 | 架構權衡 |
|---|---|---|---|---|---|
| 抽象工廠 | 建立型 | 在不指定具體類別的情況下,建立相關物件家族。 | 多層級服務套件(例如:基礎版 vs. 精英版報稅方案)。 | 消除產品家族不相容;強制一致性物件建立。 | 介面過度繁衍與間接呼叫成本。 |
| 策略 | 行為型 | 將可互換的演算法封裝在統一介面之後。 | 多支付閘道路由器(Stripe、PayPal、Adyen)。 | 移除單體式 if-else/switch 區塊;遵循開閉原則。 |
簡單演算法變體導致類別/模組爆炸。 |
| 轉接器 | 結構型 | 在兩個不相容的介面之間進行轉譯。 | 整合舊有廠商 KYC SDK 的 onboarding 服務。 | 隔離領域邏輯,避免受外部/舊版 API 變動影響。 | 資料對應成本與潛在抽象洩漏。 |
| 觀察者 | 行為型 | 定義一對多依賴關係,將狀態變更廣播給訂閱者。 | 電商訂單結帳時的事件通知。 | 將領域核心與下游副作用解耦。 | 執行流程隱晦,增加除錯與追蹤難度。 |
| 裝飾器 | 結構型 | 在不改變類別結構的前提下,於執行時期動態新增行為。 | 含選擇性詐欺偵測與稽核的支付處理管線。 | 避免子類別排列爆炸;支援動態組合。 | 破壞物件識別(== 檢查失效);順序依賴風險。 |
1. 抽象工廠模式
核心概念
抽象工廠模式提供一個介面,用來建立一組相關或相依的物件家族,而無需指定其具體類別。它保證一起設計的產品集合能被一致地建立,並將建立邏輯與消費者程式碼解耦。
情境:分級報稅服務套件
假設某服務商提供兩種服務等級:
- 報稅服務:基礎版(標準 W-2)vs. 精英版(跨州、加密貨幣、股票選擇權)。
- 節稅服務:基礎版(401k 建議)vs. 精英版(遺產規劃與境外避稅)。
❌ 違反架構:分散建立與家族不相容風險
在商業邏輯中直接建立具體產品,容易因人為失誤導致開發者不小心把 BasicTaxFiling 與 EliteTaxSaving 混用。
public class TaxServiceOrchestrator {
public void executeService(String tier) {
TaxFilingService filing;
TaxSavingService saving;
// Prone to human error: a developer could accidentally mix BASIC filing with ELITE saving
if ("BASIC".equalsIgnoreCase(tier)) {
filing = new BasicTaxFiling();
saving = new BasicTaxSaving();
} else if ("ELITE".equalsIgnoreCase(tier)) {
filing = new EliteTaxFiling();
saving = new EliteTaxSaving();
} else {
throw new IllegalArgumentException("Invalid service tier: " + tier);
}
filing.fileTaxes();
saving.provideAdvice();
}
}
Enter fullscreen mode Exit fullscreen mode
✅ 重構後架構:抽象工廠強制一致性
結構架構圖
graph TD
subgraph Client Code
Orchestrator[TaxServiceOrchestrator]
end
subgraph Factory Hierarchy
FactoryInterface[FullServicePackageFactory Interface]
BasicFactory[BasicPackageFactory]
EliteFactory[ElitePackageFactory]
FactoryInterface <|-- BasicFactory
FactoryInterface <|-- EliteFactory
end
subgraph Product Families
FilingInterface[TaxFilingService]
SavingInterface[TaxSavingService]
BasicFiling[BasicTaxFiling]
EliteFiling[EliteTaxFiling]
BasicSaving[BasicTaxSaving]
EliteSaving[EliteTaxSaving]
FilingInterface <|-- BasicFiling
FilingInterface <|-- EliteFiling
SavingInterface <|-- BasicSaving
SavingInterface <|-- EliteSaving
end
Orchestrator --> FactoryInterface
BasicFactory ..> BasicFiling
BasicFactory ..> BasicSaving
EliteFactory ..> EliteFiling
EliteFactory ..> EliteSaving
Enter fullscreen mode Exit fullscreen mode
程式碼實作
// 1. Abstract Product 1: Tax Filing
public interface TaxFilingService {
void fileTaxes();
}
// 2. Abstract Product 2: Tax Saving
public interface TaxSavingService {
void provideAdvice();
}
// 3. Concrete Products for BASIC Tier
public class BasicTaxFiling implements TaxFilingService {
@Override
public void fileTaxes() {
System.out.println("Filing standard W-2 income taxes.");
}
}
public class BasicTaxSaving implements TaxSavingService {
@Override
public void provideAdvice() {
System.out.println("Providing basic 401(k) contribution advice.");
}
}
// 4. Concrete Products for ELITE Tier
public class EliteTaxFiling implements TaxFilingService {
@Override
public void fileTaxes() {
System.out.println("Filing multi-state, crypto, and stock-option taxes.");
}
}
public class EliteTaxSaving implements TaxSavingService {
@Override
public void provideAdvice() {
System.out.println("Providing estate planning and offshore tax shelter advice.");
}
}
// 5. The Abstract Factory Interface
public interface FullServicePackageFactory {
TaxFilingService createFilingService();
TaxSavingService createSavingService();
}
// 6. Concrete Factory 1: Basic Package Family
public class BasicPackageFactory implements FullServicePackageFactory {
@Override
public TaxFilingService createFilingService() {
return new BasicTaxFiling();
}
@Override
public TaxSavingService createSavingService() {
return new BasicTaxSaving();
}
}
// 7. Concrete Factory 2: Elite Package Family
public class ElitePackageFactory implements FullServicePackageFactory {
@Override
public TaxFilingService createFilingService() {
return new EliteTaxFiling();
}
@Override
public TaxSavingService createSavingService() {
return new EliteTaxSaving();
}
}
// 8. Clean Client Code (Guaranteed family cohesion, zero risk of mixing tiers)
public class TaxOrchestrator {
private final TaxFilingService filingService;
private final TaxSavingService savingService;
// Injected with a cohesive factory family
public TaxOrchestrator(FullServicePackageFactory packageFactory) {
this.filingService = packageFactory.createFilingService();
this.savingService = packageFactory.createSavingService();
}
public void runFullService() {
filingService.fileTaxes();
savingService.provideAdvice();
}
}
Enter fullscreen mode Exit fullscreen mode
為什麼需要它?
- 消除具體耦合:將商業邏輯與具體產品類別解耦。
- 可擴展性:新增產品線/等級時,無需改動客戶端商業程式碼。
- 防止產品混搭:強制由同一工廠建立的物件屬於同一產品家族。
- 符合 SOLID:遵循開閉原則與依賴反轉原則。
架構權衡
- 間接呼叫成本:開發者需穿越介面與工廠抽象層,才能檢視底層具體實例。
- 介面過度繁衍:需為工廠及每個新產品線建立抽象介面。
何時應避免或拒絕
- 簡單、不變物件:為簡單 POJO、DTO 或具標準建構子的領域實體建立工廠屬過度設計。
- 使用 IoC/DI 框架:現代框架(如 Spring、NestJS、Wire)已作為全域 IoC 容器,為由 DI 容器管理的單例服務撰寫自訂抽象工廠是多餘的。
2. 策略模式
核心概念
策略模式將演算法選擇與演算法執行解耦。取代在核心商業邏輯中嵌入複雜條件判斷,它將每種演算法變體封裝在共同介面之後。
情境:多支付閘道路由器
支付路由系統需依使用者情境或地區,透過不同支付提供者(Stripe、PayPal、Adyen)處理交易。
❌ 違反架構:條件單體
public class PaymentProcessor {
public PaymentResult processPayment(String gateway, BigDecimal amount, PaymentInfo info) {
if ("STRIPE".equalsIgnoreCase(gateway)) {
// 50 lines of Stripe SDK calls, header setup, and signature generation
System.out.println("Processing via Stripe SDK...");
return new PaymentResult(true, "ch_stripe_123");
} else if ("PAYPAL".equalsIgnoreCase(gateway)) {
// 60 lines of PayPal OAuth token fetching & payload mapping
System.out.println("Processing via PayPal REST API...");
return new PaymentResult(true, "pay_paypal_456");
} else if ("ADYEN".equalsIgnoreCase(gateway)) {
// 40 lines of Adyen HMAC signature generation & request building
System.out.println("Processing via Adyen API...");
return new PaymentResult(true, "adyen_tx_789");
} else {
throw new IllegalArgumentException("Unsupported payment gateway: " + gateway);
}
}
}
Enter fullscreen mode Exit fullscreen mode
✅ 重構後架構:封裝策略登錄
graph LR
Client[PaymentService] --> Registry[PaymentRegistry]
Registry --> StrategyInterface[PaymentStrategy Interface]
StrategyInterface <|.. Stripe[StripeStrategy]
StrategyInterface <|.. PayPal[PayPalStrategy]
StrategyInterface <|.. Adyen[AdyenStrategy]
Enter fullscreen mode Exit fullscreen mode
程式碼實作
// 1. Common Strategy Interface
public interface PaymentStrategy {
void pay(double amount);
}
// 2. Concrete Strategy A
public class StripeStrategy implements PaymentStrategy {
@Override
public void pay(double amount) {
// Logic specific to Stripe API
System.out.println("Paid $" + amount + " using Stripe.");
}
}
// Concrete Strategy B
public class PayPalStrategy implements PaymentStrategy {
@Override
public void pay(double amount) {
// Logic specific to PayPal API
System.out.println("Paid $" + amount + " using PayPal.");
}
}
// 3. Simple Strategy Registry
public class PaymentRegistry {
private final Map<String, PaymentStrategy> strategies = new HashMap<>();
public PaymentRegistry() {
// Register strategies by key
strategies.put("STRIPE", new StripeStrategy());
strategies.put("PAYPAL", new PayPalStrategy());
}
public PaymentStrategy get(String type) {
PaymentStrategy strategy = strategies.get(type.toUpperCase());
if (strategy == null) {
throw new IllegalArgumentException("Invalid payment type: " + type);
}
return strategy;
}
}
// 4. Client Code
public class PaymentService {
private final PaymentRegistry registry = new PaymentRegistry();
public void processPayment(String gatewayType, double amount) {
// Fetch the matching strategy and execute it
PaymentStrategy strategy = registry.get(gatewayType);
strategy.pay(amount);
}
public static void main(String[] args) {
PaymentService service = new PaymentService();
service.processPayment("STRIPE", 100.0); // Output: Paid $100.0 using Stripe.
service.processPayment("PAYPAL", 50.0); // Output: Paid $50.0 using PayPal.
}
}
Enter fullscreen mode Exit fullscreen mode
為什麼需要它?
-
消除控制流膨脹:以查詢機制取代複雜的
if-else或switch區塊。 - 隔離測試:可獨立單元測試個別演算法,而不需實例化周邊服務。
-
符合開閉原則:新增整合(如
NetBankingStrategy)只需新增類別並註冊即可。
架構權衡
- 類別/模組爆炸:以多個具體策略類別、登錄表與介面契約,換取單一多分支檔案。
何時應避免或拒絕
- 不變/微不足道邏輯:若變體簡單且很少變更,標準條件判斷已足夠。
- 函數式程式設計典範:在支援一級函式的語言(如 TypeScript、Rust、Go、現代 Java)中,為策略建立沉重類別階層是反模式,應優先傳遞 lambda 或高階函式。
3. 轉接器模式
核心概念
轉接器模式作為不相容介面之間的結構轉譯器。它允許核心領域契約保持乾淨,同時在第三方 API 或舊系統之間轉譯請求與回應。
情境:KYC 驗證整合
onboarding 服務需使用舊有外部廠商 SDK(LegacyPersonaSdk)進行第三方 KYC 身分驗證。
❌ 違反架構:領域受外部契約污染
// Legacy vendor class (Unmodifiable external SDK)
class LegacyPersonaSdk {
public int executeCheck(String passportNum, String countryIso2) {
// Returns 200 for pass, 401 for fail, 500 for error
System.out.println("Calling Legacy Persona API...");
return 200;
}
}
// Violated Business Logic directly polluted with vendor-specific parameters and status codes
public class OnboardingService {
private final LegacyPersonaSdk legacySdk = new LegacyPersonaSdk();
public boolean verifyUser(String passportNumber, String countryCode) {
// Direct coupling to legacy method signature and vendor status codes
int responseCode = legacySdk.executeCheck(passportNumber, countryCode);
return responseCode == 200;
}
}
Enter fullscreen mode Exit fullscreen mode
✅ 重構後架構:領域轉接器隔離
graph LR
Client[OnboardingService] --> TargetInterface[KycVerifier Interface]
Adapter[LegacyPersonaAdapter] ..|> TargetInterface
Adapter --> Adaptee[LegacyPersonaSdk]
Enter fullscreen mode Exit fullscreen mode
程式碼實作
// 1. Clean Application Target Contract
public interface KycVerifier {
KycResult verifyUser(UserIdentity identity);
}
// Domain Models
record UserIdentity(String passportNumber, String countryCode) {}
record KycResult(boolean status, String referenceId) {}
// 2. Unmodifiable External API / Adaptee
class LegacyPersonaSdk {
public int executeCheck(String passportNum, String countryIso2) {
System.out.println("Calling Legacy Persona API...");
return 200;
}
}
// 3. Concrete Adapter Implementing Target Contract
public class LegacyPersonaAdapter implements KycVerifier {
private final LegacyPersonaSdk legacySdk;
public LegacyPersonaAdapter(LegacyPersonaSdk legacySdk) {
this.legacySdk = legacySdk;
}
@Override
public KycResult verifyUser(UserIdentity identity) {
// Translate domain model to legacy SDK parameters
int rawStatusCode = legacySdk.executeCheck(identity.passportNumber(), identity.countryCode());
// Translate legacy response codes into clean domain response objects
boolean passed = (rawStatusCode == 200);
return new KycResult(passed, "PERSONA-TX-" + System.currentTimeMillis());
}
}
// 4. Clean Business Logic (Completely decoupled from legacy vendor code)
public class OnboardingService {
private final KycVerifier kycVerifier;
// Injected via Interface
public OnboardingService(KycVerifier kycVerifier) {
this.kycVerifier = kycVerifier;
}
public boolean onboardUser(UserIdentity identity) {
KycResult result = kycVerifier.verifyUser(identity);
return result.status();
}
}
Enter fullscreen mode Exit fullscreen mode
為什麼需要它?
- 廠商中立:防止內部領域契約被外部廠商模型主導。
- 因應變更的韌性:更換廠商只需撰寫新的轉接器實作;核心商業程式碼維持不變。
架構權衡
- 資料對應成本:將內部模型轉譯為廠商特定參數,會在物件對應時增加少量 CPU 與記憶體開銷。
- 抽象洩漏:若目標舊系統缺乏關鍵功能,轉接器可能需進行變通或隱藏缺失功能。
何時應避免或拒絕
- 直接 1:1 對應介面:若轉接器僅透傳參數而無簽章或格式變更,則增加不必要的樣板程式碼。
- 內部程式碼所有權:若呼叫端與接收端程式碼皆由你擁有與控制,應直接重構目標 API,而非包裝成轉接器。
4. 觀察者模式
核心概念
觀察者模式定義一對多發布-訂閱依賴關係。當核心主體狀態改變時,它會向已註冊的觀察者廣播事件通知,而無需知道觀察者的具體細節或執行機制。
情境:訂單結帳副作用
當電商應用程式中建立訂單時,需執行下游動作:寄送顧客電子郵件通知並更新倉庫庫存。
❌ 違反架構:寫死的副作用
public class OrderService {
private final EmailService emailService = new EmailService();
private final InventoryService inventoryService = new InventoryService();
public void placeOrder(String orderId, String customerId, double amount) {
// Core Business Transaction
System.out.println("Order " + orderId + " saved to DB.");
// BAD DESIGN: Core service explicitly knows and synchronously calls downstream tasks
emailService.sendConfirmationEmail(customerId, orderId);
inventoryService.deductStock(orderId);
}
}
Enter fullscreen mode Exit fullscreen mode
✅ 重構後架構:解耦發布-訂閱系統
graph TD
OrderService[OrderService] --> Publisher[OrderPublisher]
Publisher --> ListenerInterface[OrderEventListener]
ListenerInterface <|.. EmailListener[EmailNotificationListener]
ListenerInterface <|.. InventoryListener[InventoryUpdateListener]
Enter fullscreen mode Exit fullscreen mode
程式碼實作
// 1. Immutable Event Payload
public record OrderPlacedEvent(String orderId, String customerId, double amount) {}
// 2. Observer (Subscriber) Interface
public interface OrderEventListener {
void onOrderPlaced(OrderPlacedEvent event);
}
// 3. Concrete Observers (Decoupled Side-Effects)
public class EmailNotificationListener implements OrderEventListener {
@Override
public void onOrderPlaced(OrderPlacedEvent event) {
System.out.println("Sending email to customer " + event.customerId() + " for order " + event.orderId());
}
}
public class InventoryUpdateListener implements OrderEventListener {
@Override
public void onOrderPlaced(OrderPlacedEvent event) {
System.out.println("Deducting inventory stock for order " + event.orderId());
}
}
// 4. Subject / Publisher (Manages Subscribers & Broadcasts Events)
public class OrderPublisher {
private final List<OrderEventListener> listeners = new ArrayList<>();
public void registerListener(OrderEventListener listener) {
listeners.add(listener);
}
public void publishOrderPlaced(OrderPlacedEvent event) {
for (OrderEventListener listener : listeners) {
try {
listener.onOrderPlaced(event);
} catch (Exception e) {
// Isolate subscriber failures so they don't break other listeners
System.err.println("Subscriber failed: " + e.getMessage());
}
}
}
}
// 5. Clean Core Business Logic
public class OrderService {
private final OrderPublisher publisher;
public OrderService(OrderPublisher publisher) {
this.publisher = publisher);
}
public void placeOrder(String orderId, String customerId, double amount) {
// Core Business Logic
System.out.println("Order " + orderId + " saved to DB.");
// Clean event broadcast: OrderService has NO direct dependency on Email or Inventory services
publisher.publishOrderPlaced(new OrderPlacedEvent(orderId, customerId, amount));
}
}
Enter fullscreen mode Exit fullscreen mode
為什麼需要它?
- 移除元件之間的緊密耦合。
- 每個監聽器可分別建立與測試,且各自的失敗可被獨立處理,新監聽器的加入不會破壞既有流程。
- 有助於遵循單一職責原則、開閉原則、依賴反轉原則。
架構權衡
- 隱藏的執行鏈與除錯複雜度:由於訂閱者是動態註冊的,透過日誌檔案追蹤控制流程變得困難。你無法再簡單地在 IDE 中「前往定義」查看狀態改變後的所有後續行為。
何時應避免或拒絕
- 若程序只有一個永久且不會改變的副作用,加入觀察者登錄表會造成不必要的間接呼叫。
擴展:從記憶體觀察者到分散式架構
在微服務架構中,記憶體觀察者會演進為分散式事件驅動架構(EDA)。服務不再使用記憶體登錄表,而是將訊息發布到如 Apache Kafka、AWS SNS/SQS 或 RabbitMQ 等分散式訊息代理。
為在不遺失事件的情況下維持系統一致性,正式架構常搭配交易式 Outbox 模式:
- 商業資料與事件紀錄在同一個資料庫交易中寫入。
- 另一個中繼程序讀取 outbox 條目,並發布到訊息代理。
graph LR
subgraph Microservice Context
Service[Order Service] --> DB[(Database / Outbox Table)]
end
DB --> Relay[Debezium / Outbox Publisher]
Relay --> Broker[Apache Kafka / RabbitMQ]
Broker --> Listener1[Notification Service]
Broker --> Listener2[Inventory Service]
Enter fullscreen mode Exit fullscreen mode
5. 裝飾器模式
核心概念
裝飾器模式允許在執行時期動態為物件新增職責,而無需修改其核心程式碼或引入靜態子類別爆炸。它以多層包裝核心元件,新增橫切關注點行為。
情境:信用卡處理選擇性附加功能
信用卡處理服務可於執行時期加入選擇性附加功能,例如詐欺檢查與稽核記錄。
❌ 違反架構:子類別排列混亂
為每種選擇性行為組合建立子類別,會導致類別數量呈指數成長。
graph TD
A[PaymentProcessor Base] --> B[FraudCheckingPaymentProcessor]
A --> C[AuditLoggingPaymentProcessor]
A --> D[MetricsPaymentProcessor]
B --> E[Fraud + Logging PaymentProcessor]
C --> E
C --> F[Logging + Metrics PaymentProcessor]
D --> F
B --> G[Fraud + Metrics PaymentProcessor]
D --> G
E --> H[Fraud + Logging + Metrics PaymentProcessor]
F --> H
G --> H
Enter fullscreen mode Exit fullscreen mode
public class BasicPaymentProcessor {
public void processPayment(double amount) {
System.out.println("Processing credit card payment of $" + amount);
}
}
// Subclass permutations needed for every combination!
public class LoggingPaymentProcessor extends BasicPaymentProcessor { /* ... */ }
public class FraudCheckingPaymentProcessor extends BasicPaymentProcessor { /* ... */ }
public class LoggingAndFraudCheckingPaymentProcessor extends BasicPaymentProcessor { /* ... */ }
// Adding 3 more features leads to subclass explosion!
Enter fullscreen mode Exit fullscreen mode
✅ 重構後架構:動態組合鏈
graph LR
Client[Client Code] --> FraudDecorator[FraudCheckDecorator]
FraudDecorator --> AuditDecorator[AuditLoggingDecorator]
AuditDecorator --> Core[StandardPaymentProcessor]
Enter fullscreen mode Exit fullscreen mode
程式碼實作
// 1. Component Interface
public interface PaymentProcessor {
void processPayment(double amount);
}
// 2. Concrete Base Component (Core Domain Logic)
public class StandardPaymentProcessor implements PaymentProcessor {
@Override
public void processPayment(double amount) {
System.out.println("Executing core payment debit of $" + amount);
}
}
// 3. Abstract Decorator (Maintains reference to wrapped component)
public abstract class PaymentProcessorDecorator implements PaymentProcessor {
protected final PaymentProcessor wrappedProcessor;
public PaymentProcessorDecorator(PaymentProcessor wrappedProcessor) {
this.wrappedProcessor = wrappedProcessor;
}
@Override
public void processPayment(double amount) {
wrappedProcessor.processPayment(amount); // Default delegation
}
}
// 4. Concrete Decorator A
public class FraudCheckDecorator extends PaymentProcessorDecorator {
public FraudCheckDecorator(PaymentProcessor wrappedProcessor) {
super(wrappedProcessor);
}
@Override
public void processPayment(double amount) {
System.out.println("[FRAUD CHECK] Validating risk score for amount: $" + amount);
super.processPayment(amount); // Delegate to next layer
}
}
// Concrete Decorator B
public class AuditLoggingDecorator extends PaymentProcessorDecorator {
public AuditLoggingDecorator(PaymentProcessor wrappedProcessor) {
super(wrappedProcessor);
}
@Override
public void processPayment(double amount) {
System.out.println("[AUDIT LOG] Payment initiated at " + System.currentTimeMillis());
super.processPayment(amount); // Delegate to next layer
System.out.println("[AUDIT LOG] Payment execution finished.");
}
}
// 5. Usage / Pipeline Construction
public class PaymentApplication {
public static void main(String[] args) {
// Base Processor
PaymentProcessor processor = new StandardPaymentProcessor();
// Dynamically compose processing pipeline at runtime: Fraud Check -> Audit Log -> Core Payment
PaymentProcessor securePipeline = new FraudCheckDecorator(
new AuditLoggingDecorator(processor)
);
securePipeline.processPayment(250.00);
}
}
Enter fullscreen mode Exit fullscreen mode
為什麼需要它?
- 防止類別爆炸:以動態組合取代靜態繼承組合。
- 單一職責原則:橫切關注點(記錄、詐欺檢查、指標)被分離到個別包裝類別。
架構權衡
-
身分危機:被裝飾的實例與內部包裝元件不共享物件識別(
decoratedObject != innerObject)。相等性檢查(==)或基於反射的邏輯可能失效。 - 順序開銷:若裝飾器依賴特定執行順序,巢狀組合可能引入細微的執行時期錯誤。
何時應避免或拒絕
- 嚴格的順序依賴:若裝飾器 A 必須總是在裝飾器 B 之前執行,且順序錯亂會破壞系統狀態,則包裝管線會增加風險。
- 框架已提供攔截器:現代企業框架提供面向切面程式設計(AOP)或中介軟體/過濾器管線(如 Spring AOP、Express Middlewares、gRPC Interceptors),可在不手動實例化物件包裝器的情況下,乾淨地處理橫切關注點。
💡 取得完整 10 頁系統設計指南
訂閱 The Tech Builder Newsletter,即可立即免費取得完整、未刪節的指南。
每週,訂閱者將收到:
- 🎯 深入的生產環境事後檢討與系統設計權衡分析。
- 🛠️ 針對資深 IC、技術主管與架構師的真實世界架構手冊。
- 🎁 即時贈品:訂閱後立即取得完整 6 個月準備追蹤器與學習計畫+10 頁系統設計速查表。
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.