C#_組合優于繼承的實際應用


2.2 Composition over Inheritance:組合優于繼承的實際應用

繼承(Inheritance)是面向對象編程中最容易被過度使用和誤用的特性之一。傳統的教學往往讓人們優先選擇繼承來實現代碼復用和建立“是一個(is-a)”的關系。然而,在復雜的業務系統中,嚴格的繼承層次結構常常會變得僵化、脆弱,最終難以維護。

“組合優于繼承”(Composition over Inheritance)是一條經典的設計原則,它倡導通過將對象組合在一起,而不是通過繼承來擴展功能。它旨在構建“有一個(has-a)”或“使用一個(uses-a)”的關系,從而獲得更大的靈活性。

2.2.1 繼承的陷阱:脆弱的基類問題

讓我們看一個典型的濫用繼承的例子:

// 一個看似合理的繼承層次結構
public abstract class OrderProcessorBase {public virtual void Validate(Order order) {// 基礎驗證邏輯}public virtual decimal CalculateTotal(Order order) {// 計算總價的基礎邏輯return order.Items.Sum(i => i.Price * i.Quantity);}public void Process(Order order) {Validate(order);order.Total = CalculateTotal(order);// ... 其他處理步驟,如保存、通知等}
}// 為特定場景擴展
public class OnlineOrderProcessor : OrderProcessorBase {public override void Validate(Order order) {base.Validate(order); // 先執行基礎驗證// 添加在線訂單特有的驗證,如配送地址必填if (string.IsNullOrEmpty(order.ShippingAddress))throw new InvalidOperationException("Shipping address is required.");}public override decimal CalculateTotal(Order order) {decimal baseTotal = base.CalculateTotal(order);// 添加在線訂單特有的費用,如運費return baseTotal + 5.0m;}
}public class TaxFreeOrderProcessor : OrderProcessorBase {public override decimal CalculateTotal(Order order) {// 免稅訂單,需要完全重寫計算邏輯,可能無法有效復用基類邏輯return order.Items.Sum(i => i.PriceBeforeTax * i.Quantity);}
}

這個設計的問題在于:

  1. 脆弱的基類(Fragile Base Class):對 OrderProcessorBase 的任何修改(例如,添加一個新的虛擬方法,修改 Process 方法的流程)都可能會無聲地破壞所有子類的行為。子類與基類緊密耦合,基類變得“脆弱”。
  2. 爆炸性的子類:如果現在需要處理一種“在線且免稅”的訂單,你該怎么辦?創建 OnlineTaxFreeOrderProcessor?這會導致類層次結構爆炸,產生復雜的菱形繼承問題(C#不支持多繼承,此路不通)。
  3. 不靈活的復用TaxFreeOrderProcessor 無法復用基類 CalculateTotal 的邏輯,只能重寫,可能導致代碼重復。
  4. 單一演化方向:繼承強制子類沿著基類的單一維度進行演化。但現實世界的需求變化往往是多維度的(如支付方式、客戶等級、訂單來源等)。

2.2.2 組合的解決方案:策略模式與行為注入

讓我們用“組合”來重新設計上面的例子。我們將不同的行為(驗證、計算)抽取出來,定義為獨立的策略接口。

// 1. 定義抽象策略接口
public interface IOrderValidationStrategy {void Validate(Order order);
}
public interface IOrderCalculationStrategy {decimal CalculateTotal(Order order);
}
// ... 可以定義更多策略接口,如 INotificationStrategy, IPersistenceStrategy// 2. 實現各種具體策略
public class StandardValidationStrategy : IOrderValidationStrategy {public void Validate(Order order) { /* 基礎驗證邏輯 */ }
}
public class OnlineOrderValidationStrategy : IOrderValidationStrategy {public void Validate(Order order) {// 包含基礎驗證和在線特有驗證new StandardValidationStrategy().Validate(order);if (string.IsNullOrEmpty(order.ShippingAddress))throw new InvalidOperationException("Shipping address is required.");}
}
//---
public class StandardCalculationStrategy : IOrderCalculationStrategy {public decimal CalculateTotal(Order order) => order.Items.Sum(i => i.Price * i.Quantity);
}
public class OnlineCalculationStrategy : IOrderCalculationStrategy {public decimal CalculateTotal(Order order) => new StandardCalculationStrategy().CalculateTotal(order) + 5.0m;
}
public class TaxFreeCalculationStrategy : IOrderCalculationStrategy {public decimal CalculateTotal(Order order) => order.Items.Sum(i => i.PriceBeforeTax * i.Quantity);
}// 3. 核心訂單處理器,通過組合各種策略來完成工作
public class OrderProcessor {private readonly IOrderValidationStrategy _validationStrategy;private readonly IOrderCalculationStrategy _calculationStrategy;// ... 其他策略// 策略通過構造函數注入。這提供了極大的靈活性!public OrderProcessor(IOrderValidationStrategy validationStrategy,IOrderCalculationStrategy calculationStrategy){_validationStrategy = validationStrategy;_calculationStrategy = calculationStrategy;}public void Process(Order order) {_validationStrategy.Validate(order);order.Total = _calculationStrategy.CalculateTotal(order);// ... 使用其他策略}
}

現在,如何創建那個令人頭疼的“在線且免稅”訂單處理器?變得非常簡單:

// 在應用程序的 composition root (通常是DI容器配置處) 進行組裝
var processorForComplexOrder = new OrderProcessor(validationStrategy: new OnlineOrderValidationStrategy(),    // 使用在線驗證calculationStrategy: new TaxFreeCalculationStrategy()      // 使用免稅計算
);// 或者,你可以創建新的組合策略,而不是新的Processor類
public class OnlineTaxFreeCalculationStrategy : IOrderCalculationStrategy {public decimal CalculateTotal(Order order) {decimal baseTotal = new StandardCalculationStrategy().CalculateTotal(order);return baseTotal + 5.0m; // 在線費用,但免稅邏輯已融入?這里設計需要斟酌}
}
// 更好的方式是:認識到“加運費”和“免稅”是兩個正交的關切點,可能需要更細粒度的策略組合。

2.2.3 組合的優勢與架構師視角

  1. 靈活性(Flexibility):你可以動態地組合任何驗證策略和任何計算策略,輕松應對“在線且免稅”這種多維度的需求變化,而無需修改現有類或創建復雜的繼承樹。
  2. 解耦(Decoupling)OrderProcessor 不再與任何具體的業務實現耦合,它只依賴于接口。各個策略之間也是相互獨立的。
  3. 可測試性(Testability):可以輕松地為 OrderProcessor 編寫單元測試,只需注入Mock的策略對象即可。
  4. 單一職責(Single Responsibility):每個策略類都只有一個非常明確的職責,符合SRP。
  5. 開閉原則(Open/Closed):添加新的驗證或計算方式(新的策略實現)完全不需要修改 OrderProcessor 或其他策略,符合OCP。

何時使用繼承?
“組合優于繼承”并非要完全否定繼承。繼承在以下場景仍然有效:

  • 建立嚴格的類型層次結構:當存在真正的“is-a”關系,且子類確實是基類的一個更具體的類型時(如 Cat : Animal, Button : Control)。
  • 需要多態:當你需要讓不同的子類對象對同一消息做出不同響應時。
  • 框架設計:用于定義模板方法模式,其中基類控制主要流程,子類只負責實現特定的步驟。

決策指南:

  • 默認優先選擇組合,尤其是對于行為的擴展。
  • 詢問自己:“我是為了復用代碼,還是為了建立類型關系?”如果主要是為了復用,組合通常是更安全、更靈活的選擇。
  • 如果使用繼承,確保子類和基類的關系是穩定且永久的,避免設計深度過大的繼承層次。
  • 利用依賴注入框架來管理這些可組合的策略對象,這將使你的系統像由樂高積木組成一樣,可以輕松地拆卸和重組。

總結:
從“繼承思維”轉變為“組合思維”是邁向高級軟件設計的關鍵一步。它要求你從對象間的關系和行為的角度來思考,而不是僅從分類的角度。通過將系統分解為一系列精細、單一職責、可插拔的組件,并通過組合它們來構建復雜行為,你將創建一個真正靈活、健壯且易于演進的架構。這種思維方式是理解后續依賴注入(DI)和更高級架構模式的基礎。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/919778.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/919778.shtml
英文地址,請注明出處:http://en.pswp.cn/news/919778.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Kafka消息丟失的場景有哪些

生產者在生產過程中的消息丟失 broker在故障后的消息丟失 消費者在消費過程中的消息丟失ACK機制 ack有3個可選值,分別是1,0,-1。 ack0:生產者在生產過程中的消息丟失 簡單來說就是,producer發送一次就不再發送了&#…

盼之代售 231滑塊 csessionid 分析

聲明 本文章中所有內容僅供學習交流使用,不用于其他任何目的,抓包內容、敏感網址、數據接口等均已做脫敏處理,嚴禁用于商業用途和非法用途,否則由此產生的一切后果均與作者無關! 逆向分析 部分python代碼 url "…

STL關聯式容器解析:map與set詳解

目錄 1. 關聯式容器 2. 鍵值對 3. 樹形結構的關聯式容器 3.1 set 3.1.2 set的使用 3.2 map 3.2.1 map的介紹 3.2.2 map的使用 3.3 multiset 3.3.1 multiset的介紹 3.3.2 multiset的使用 3.4 multimap 3.4.1 multimap的介紹 3.4.2 multimap的使用 4.紅黑樹模擬實現…

貪吃蛇--C++實戰項目(零基礎)

視頻地址:C語言必學項目:貪吃蛇! 貪吃蛇游戲框架 ├── 基礎框架 │ ├── 頭文件引入 │ ├── 常量和宏定義 │ └── 窗口初始化 │ ├── 數據結構系統 │ ├── Pos結構體(位置和顏色) │ ├── Snake結構體(蛇的屬性) │ ├──…

unity資源領取反作弊工具加密器

https://assetstore.unity.com/packages/tools/utilities/anti-cheat-pro-2025-3006260元購碼GUARDINGPEARSOFTWARE

FPGA設計中的信號完整性量化與優化:探索高速數字系統的關鍵路徑

在高速FPGA設計中,信號完整性(Signal Integrity, SI)已成為確保系統穩定、可靠運行的核心要素之一。隨著數據傳輸速率的不斷提升和電路規模的日益復雜,信號在傳輸過程中受到的干擾和畸變問題日益凸顯。因此,如何有效量…

`strncasecmp` 字符串比較函數

1) 函數的概念與用途 strncasecmp 是 C 語言中一個非常實用的字符串處理函數,它執行不區分大小寫的字符串比較,但只比較前 n 個字符。這個函數的名字來源于"string n case-compare"(字符串前n個字符不區分大小寫比較)。…

軟件安裝教程(一):Visual Studio Code安裝與配置(Windows)

文章目錄前言一、Visual Studio Code下載二、安裝步驟(Windows)1. GUI安裝2. 命令行安裝三、首次啟動后建議的快速配置(幾分鐘完成)四、常見問題 & 小貼士總結前言 Visual Studio Code(VS Code)是一款…

JavaSSM框架從入門到精通!第三天(MyBatis(二))!

四、Mapper 的動態代理1. 引入 在上面的 CURD 例子中,我們發現:Dao 層的實現類的每一個方法僅僅是通過 SqlSession 對象的相關 API 定位到映射文件 mapper 中的 SQL 語句,真正對數據庫操作的工作實際上是有 Mybatis 框架通過 mapper 中的 SQL…

大模型應用發展與Agent前沿技術趨勢(下)

Agent技術的行業應用與實踐案例 金融領域的Agent應用 金融行業是大模型Agent技術應用最為廣泛的領域之一,涵蓋了風險評估、投資決策、客戶服務等多個方面。在金融風控領域,Agent系統通過結合大模型的語義理解能力和強化學習的決策優化能力,能…

94. 城市間貨物運輸 I, Bellman_ford 算法, Bellman_ford 隊列優化算法

94. 城市間貨物運輸 IBellman_ford 算法Bellman_ford 算法 與 dijkstra算法 相比通用性更強。dijkstra算法解決不了負權邊的問題,因為Dijkstra基于貪心策略,一旦一個節點被從隊列中取出(標記為已解決),它就假定已經找到…

如何使用Prometheus + Grafana + Loki構建一個現代化的云原生監控系統

如何使用 Prometheus + Grafana + Loki 構建一個現代化的云原生監控系統。這套組合被譽為監控領域的“瑞士軍刀”,功能強大且生態極佳。 一、核心組件概念介紹 在搭建之前,深刻理解每個組件的角色和職責至關重要。 1. Prometheus(指標監控與時序數據庫) 角色:系統的“核…

JavaScript Object 操作方法及 API

一、對象創建方式1.字面量創建(最常用)const obj { name: "張三", age: 25 };2.構造函數創建const obj new Object(); obj.name "李四";3.Object.create()(指定原型)const proto { greet: () > "…

pta乙級題目day1

第1天:輸入輸出與運算(6題)1001 害死人不償命的(3n1)猜想(基礎運算)★1006 換個格式輸出整數(格式化輸出)★1016 部分AB(數字提取)★★1046 劃拳(多輸入處理&…

在VSCode中配置.NET項目的tasks.json以實現清理、構建、熱重載和發布等操作

在 VS Code 中配置 .NET 開發任務的完整指南 引言 重要提醒:對于 .NET 開發,強烈推薦使用 Visual Studio,它提供了最完整和穩定的開發體驗。如果你像我一樣"蛋疼"想要嘗試 VS Code,請確保安裝了 C# 開發擴展包&#x…

EmEditor文本編輯器v25.3.0專業版,專業文本編輯,高亮顯示,無限撤消

[軟件名稱]: EmEditor文本編輯器v25.3.0專業版 [軟件大小]: 37.7 MB [軟件大小]: 夸克網盤 | 百度網盤 軟件介紹 EmEditor 是一款功能強大且非常實用的文本編輯器。它啟動速度快,完全可以替代 Windows 自帶的記事本,輕松應對日常文本編輯任務。它對 …

【spring security】權限管理組件執行流程詳解

🎯 權限管理組件執行流程詳解 🏗? 組件架構圖 ┌─────────────────────────────────────────────────────────────┐ │ HTTP請求 …

redis怎么保障雙寫一致性

redis做為緩存,mysql的數據如何與redis進行同步呢?(雙寫一致性)候選人:嗯!就說我最近做的這個項目,里面有xxxx(根據自己的簡歷上寫)的功能,需要讓數據庫與red…

異常值檢測:孤立森林模型(IsolationForest)總結

目錄一、前言二、孤立森林算法2.1 算法簡介2.2 基本原理2.3 算法步驟2.4 異常分數計算方式2.5 python調用方式三、python代碼示例四、小結五、參考學習一、前言 近期在研究構建壽命預測模型,相信很多數據人都懂建模的過程,其實有80%的時間都是在和數據處…

Docker容器化部署實戰:Tomcat與Nginx服務配置指南

部署Tomcat搜索鏡像 使用以下命令搜索可用的Tomcat鏡像:docker search tomcat拉取鏡像 拉取官方Tomcat鏡像:docker pull tomcat創建專用目錄 為Tomcat配置和數據創建專用目錄:mkdir tomcat運行臨時容器并復制配置文件 啟動臨時容器以復制配置…