領域驅動設計(DDD)【23】之泛化:從概念到實踐

文章目錄

  • 一 泛化基礎:理解DDD中的核心抽象機制
    • 1.1 什么是泛化?
    • 1.2 為什么泛化在DDD中重要?
    • 1.3 泛化與特化的雙向關系
  • 二 DDD中泛化的實現形式
    • 2.0 實現形式概覽
    • 2.1 類繼承:最直接的泛化實現
    • 2.2 接口實現:更靈活的泛化方式
    • 2.3 組合優于繼承:現代DDD的實踐
    • 2.4 特征值實現:馬匹管理
  • 三 DDD中泛化的高級應用
    • 3.1 限界上下文中的泛化
    • 3.2 泛化的層次設計
    • 3.3 泛化與持久化的權衡
  • 四 避免泛化的常見陷阱
    • 4.1 過度泛化:當抽象變成負擔
    • 4.2 錯誤泛化:混淆本質差異
    • 4.3 泛化導致的數據污染
  • 五、實戰案例:電商系統中的泛化設計
    • 5.1 商品類別的泛化設計
    • 5.2 訂單處理的狀態模式實現
  • 六 總結與最佳實踐
    • 6.1 何時使用泛化?
    • 6.3 泛化的替代方案
    • 6.4 持續重構的視角

一 泛化基礎:理解DDD中的核心抽象機制

  • 在領域驅動設計(Domain-Driven Design, DDD)中,泛化(Generalization)是一種強大的抽象工具,它允許我們識別和表達領域概念之間的"是一種"(is-a)關系。

1.1 什么是泛化?

  • 泛化是指從多個特定概念中提取其共性,形成更一般化的概念的過程。在面向對象編程中,這通常通過繼承來實現,但在DDD中,泛化的含義更為廣泛。現實世界類比:想象你在設計一個動物園管理系統,特定概念:獅子、老虎、長頸鹿->泛化概念:動物。這里,"獅子是一種動物"就體現了泛化關系。

1.2 為什么泛化在DDD中重要?

  1. 減少重復:共性行為可以放在父類中
  2. 提高可維護性:修改一處即可影響所有子類
  3. 增強表達力:更準確地反映業務領域的關系
  4. 支持多態:允許以統一方式處理不同子類

1.3 泛化與特化的雙向關系

Animal
+String name
+Date birthDate
+eat()
+sleep()
Lion
+roar()
Tiger
+growl()
  • 在這個類圖中,Animal是泛化類,Lion和Tiger是特化類。所有動物都有name和birthDate屬性,都能eat()和sleep(),但只有獅子能roar(),只有老虎能growl()。

二 DDD中泛化的實現形式

2.0 實現形式概覽

在這里插入圖片描述
第一種是使用類的繼承。領域模型中的父類、子類,和實現中的父類、子類直接對應。它的特點在于必須有不同種類的特性或者不同的操作。如果僅僅是某個屬性值不同造成的泛化,那么用繼承就不合適了。
第二種是接口的實現。如果各個子類在屬性和操作實現方面沒有共性,但有相同的操作接口,就使用這種方式。
第三種是用特性的值來區分,這時候在實現層面,沒有父類和子類之分。特別適用于那些行為完全相同,僅在某些屬性值上存在差異的領域概念。

2.1 類繼承:最直接的泛化實現

  • 電商系統示例
public abstract class Payment {private BigDecimal amount;private Currency currency;private LocalDateTime paymentDate;public abstract void process();
}public class CreditCardPayment extends Payment {private String cardNumber;private String cardHolder;private LocalDate expiryDate;@Overridepublic void process() {// 信用卡支付處理邏輯}
}public class PayPalPayment extends Payment {private String email;private String transactionId;@Overridepublic void process() {// PayPal支付處理邏輯}
}

優劣分析

  • 優點:直接、明確、編譯器支持
  • 缺點:Java等語言單繼承限制、父類修改影響大

2.2 接口實現:更靈活的泛化方式

public interface Shipment {void schedule();void cancel();TrackingInfo track();
}public class StandardShipment implements Shipment {// 實現標準物流
}public class ExpressShipment implements Shipment {// 實現快遞物流
}public class InternationalShipment implements Shipment {// 實現國際物流
}

適用場景

  • 當不同實現有完全不同的行為時
  • 需要多重"泛化"時
  • 強調能力而非共同結構時

2.3 組合優于繼承:現代DDD的實踐

public class Product {private String id;private String name;private PricingStrategy pricingStrategy;public BigDecimal calculatePrice(OrderContext context) {return pricingStrategy.calculate(this, context);}
}public interface PricingStrategy {BigDecimal calculate(Product product, OrderContext context);
}public class StandardPricing implements PricingStrategy {// 標準定價策略
}public class DiscountPricing implements PricingStrategy {// 折扣定價策略
}public class BundlePricing implements PricingStrategy {// 捆綁定價策略
}

優勢

  • 運行時可替換策略
  • 避免類爆炸
  • 更符合"組合優于繼承"原則

2.4 特征值實現:馬匹管理

  • 其中:所有馬都有相同的屬性和行為、唯一區別是顏色不同(棗紅馬、白馬、黑馬等)。
public class Horse {private String name;private int age;private HorseColor color;  // 類型區分字段public Horse(String name, int age, HorseColor color) {this.name = name;this.age = age;this.color = color;}public void run() { /* 奔跑實現 */ }public void eat() { /* 進食實現 */ }// 特定于顏色的行為(如果需要)public String getDescription() {return color.getDescription() + "馬";}
}public enum HorseColor {RED("棗紅"),WHITE("白"),BLACK("黑"),PALOMINO("金鬃"),DAPPLE("花斑");private final String description;HorseColor(String description) {this.description = description;}public String getDescription() {return description;}
}
public class HorseService {public void demo() {Horse redHorse = new Horse("赤兔", 5, HorseColor.RED);Horse whiteHorse = new Horse("的盧", 4, HorseColor.WHITE);System.out.println(redHorse.getDescription());   // 輸出: 棗紅馬System.out.println(whiteHorse.getDescription()); // 輸出: 白馬// 所有馬統一處理List<Horse> horses = List.of(redHorse, whiteHorse);horses.forEach(Horse::run);}
}

三 DDD中泛化的高級應用

3.1 限界上下文中的泛化

  • 在不同限界上下文中,同一概念的泛化可能不同。醫院系統示例

患者管理上下文

Person
+String name
+String id
Patient
+MedicalRecord record
Staff
+Department department

預約系統上下文

AppointableEntity
+String id
+String name
+scheduleAppointment()
Doctor
+Specialty specialty
TreatmentRoom
+String roomNumber
  • 注意在不同上下文中,泛化層次和標準完全不同。

3.2 泛化的層次設計

銀行賬戶系統示例

?abstract?
BankAccount
+String accountNumber
+BigDecimal balance
+deposit()
+withdraw()
+calculateInterest()
SavingsAccount
+BigDecimal interestRate
+calculateInterest()
CheckingAccount
+BigDecimal overdraftLimit
+withdraw()
FixedDepositAccount
+LocalDate maturityDate
+withdraw()

設計考量

  1. 抽象層次不宜過深(通常不超過3層)
  2. 每個層次都應增加明確的業務價值
  3. 避免過度設計,只為真正的業務差異創建子類

3.3 泛化與持久化的權衡

  • JPA繼承策略示例
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "account_type")
public abstract class BankAccount {@Idprivate String accountNumber;private BigDecimal balance;// 公共字段
}@Entity
@DiscriminatorValue("SAVINGS")
public class SavingsAccount extends BankAccount {private BigDecimal interestRate;// 特有字段
}@Entity
@DiscriminatorValue("CHECKING")
public class CheckingAccount extends BankAccount {private BigDecimal overdraftLimit;// 特有字段
}

三種繼承映射策略對比

策略類型數據庫結構優點缺點
SINGLE_TABLE單表,用區分字段標識子類查詢高效,無需連接字段稀疏,可能有NULL
JOINED父類子類分開表,主鍵關聯結構規范,無冗余查詢需連接,性能較差
TABLE_PER_CLASS每個具體類對應完整表查詢具體類高效多態查詢需UNION,設計復雜

四 避免泛化的常見陷阱

4.1 過度泛化:當抽象變成負擔

反例

public abstract class Entity {private Long id;// 所有實體都需要的字段
}public abstract class Person extends Entity {private String name;private String email;// 所有人共有的字段
}public abstract class User extends Person {private String username;private String password;// 所有用戶共有的字段
}public class Customer extends User {// 客戶特有字段
}public class Employee extends User {// 員工特有字段
}public class SystemAdmin extends Employee {// 系統管理員特有字段
}

問題分析

  • 層次過深,修改基類影響范圍大
  • 中間抽象類可能包含不相關功能
  • 實際業務需求可能不需要如此復雜的層次

4.2 錯誤泛化:混淆本質差異

錯誤案例

public abstract class PaymentMethod {private BigDecimal amount;public abstract void process();
}public class CreditCard extends PaymentMethod {// 信用卡支付
}public class Invoice extends PaymentMethod {// 發票支付
}public class BankTransfer extends PaymentMethod {// 銀行轉賬
}public class Coupon extends PaymentMethod {// 優惠券
}

問題

  • 優惠券本質上不是支付方式,而是折扣機制
  • 強行泛化導致業務邏輯混亂
  • 更好的設計是將Coupon作為獨立的折扣概念

4.3 泛化導致的數據污染

  • 問題示例
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Notification {@Idprivate Long id;private String recipient;private String content;private LocalDateTime sentTime;// 其他公共字段
}@Entity
public class EmailNotification extends Notification {private String emailSubject;private String ccList;
}@Entity
public class SMSNotification extends Notification {private String phoneNumber;
}@Entity
public class PushNotification extends Notification {private String deviceToken;private String appId;
}

問題

  • 單表策略下,表會包含所有子類的字段
  • 大多數記錄會有大量NULL字段
  • 隨著子類增加,表會變得臃腫

解決方案

  • 考慮使用JOINED策略
  • 或將完全不相關的通知類型拆分到不同限界上下文

五、實戰案例:電商系統中的泛化設計

5.1 商品類別的泛化設計

  • 初始設計
Product
+String id
+String name
+BigDecimal price
PhysicalProduct
+BigDecimal weight
+Dimensions dimensions
DigitalProduct
+String downloadUrl
+BigDecimal fileSize
ServiceProduct
+Duration duration
+String provider

演進設計(考慮更多業務需求后):

?abstract?
Product
+String id
+String name
+calculatePrice()
InventoryFeatures
+StockLevel stock
+reserveStock()
ShippingFeatures
+BigDecimal weight
+Dimensions dimensions
+calculateShipping()
DigitalFeatures
+String downloadUrl
+String licenseKey
+generateDownload()
Book
+String isbn
+String author
Ebook
+String format
ConsultingService
+List<TimeSlot> availability
+scheduleSession()

設計演進說明

  1. 從簡單的繼承層次轉變為更靈活的混合模式
  2. 使用接口(或mixin)表示橫切關注點
  3. 將定價策略分離出來,符合單一職責原則
  4. 每個產品可以組合不同的特性

5.2 訂單處理的狀態模式實現

public class Order {private String orderId;private OrderState state;public void cancel() {state.cancel(this);}public void approve() {state.approve(this);}public void ship() {state.ship(this);}public void deliver() {state.deliver(this);}// 狀態轉移方法void changeState(OrderState newState) {this.state = newState;}
}public interface OrderState {void cancel(Order order);void approve(Order order);void ship(Order order);void deliver(Order order);
}public class DraftState implements OrderState {// 實現草案狀態的行為
}public class ApprovedState implements OrderState {// 實現已批準狀態的行為
}public class ShippedState implements OrderState {// 實現已發貨狀態的行為
}public class DeliveredState implements OrderState {// 實現已交付狀態的行為
}public class CancelledState implements OrderState {// 實現已取消狀態的行為
}

優勢

  1. 每個狀態的行為集中在一處
  2. 避免大量的if-else條件判斷
  3. 新狀態易于添加
  4. 狀態轉換邏輯明確

六 總結與最佳實踐

6.1 何時使用泛化?

適合使用泛化的情況:

  • 存在明確的"是一種"業務關系
  • 多個概念有顯著共享的行為和屬性
  • 需要以統一接口處理不同概念時
  • 業務領域本身有明顯的分類體系

應避免泛化的情況:

  • 僅為了代碼復用而強行抽象
  • 差異大于共性的情況
  • 未來可能有根本性差異的概念
  • 技術驅動而非業務驅動的分類

6.3 泛化的替代方案

當泛化不合適時,考慮:

  1. 組合:將共性部分提取為獨立組件
  2. 策略模式:將變化的行為抽象為策略
  3. 裝飾器模式:動態添加功能
  4. 角色模式:允許對象動態獲得能力

6.4 持續重構的視角

泛化設計應是演進式的:

  1. 開始時可以扁平化設計
  2. 隨著重復代碼的出現識別共性
  3. 當業務規則差異明確時引入抽象
  4. 定期審視泛化層次是否仍然合理
  • 記住Eric Evans的話:“深層次模型是逐步演進而非一次性設計出來的,它需要開發人員和領域專家持續協作,通過多次迭代精煉而成。”

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

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

相關文章

機箱流動空氣熱學仿真方案

機箱流動空氣熱學仿真方案(二維平面與三維) 一、物理模型與數學模型 1. 控制方程 流動與傳熱基本方程: 連續性方程:?(ρu) = 0動量方程(Navier-Stokes):ρ(u?)u = -?p + μ?u + F能量方程:ρc?(u?)T = k?T + Φ邊界條件: 入口:速度入口(u=u?, T=T?)出口:壓…

electron 如何配置 打開控制臺

在 Electron 應用中&#xff0c;打開開發者工具&#xff08;即控制臺&#xff09;通常有兩種方式&#xff1a; 程序運行時手動打開 在 Electron 應用中&#xff0c;你可以通過編程方式打開開發者工具。這通常在你需要調試時非常有用。你可以在你的主進程&#xff08;通常是 ma…

MR7350用TTL刷機救磚過程

很久之前就買了一臺Linksys的MR7350路由器&#xff0c;準備有OpenWRT的官方固件之后再拿它當輕NAS用&#xff0c;最近看到出了Snapshot版&#xff0c;于是就拿來刷機試試。經過我堅持不懈的折騰&#xff0c;終于把我的MR7350路由器刷成了磚&#xff0c;即便是通過開機過程中斷電…

在NPU單算子(torch_npu )執行時如何進行性能優化?以MinerU為例

1 MinerU介紹 在AI技術快速發展的今天&#xff0c;大量非結構化數據的處理成為亟待解決的問題。尤其是PDF文檔&#xff0c;作為最常見的文件格式之一&#xff0c;如何高效準確地提取其中的信息&#xff0c;成為了許多企業和研究機構的痛點。上海人工智能實驗室&#xff08;上海…

鴻蒙OS開發IoT控制應用:從入門到實踐

引言&#xff1a;萬物互聯時代的應用開發新范式 在物聯網(IoT)技術迅猛發展的今天&#xff0c;智能設備數量呈指數級增長。據IDC預測&#xff0c;到2025年全球IoT連接設備數將達到416億臺。面對碎片化的IoT設備和多樣化的控制需求&#xff0c;華為鴻蒙OS(HarmonyOS)應運而生&a…

五層網絡模型:網絡通信的核心框架

在網絡通信的世界里&#xff0c;五層網絡模型是一個基礎而關鍵的概念。它幫助我們理解數據是如何在網絡上從一個設備傳輸到另一個設備的。本文將詳細介紹五層網絡模型的每一層&#xff0c;以及它們在數據傳輸過程中的作用。 一、五層網絡模型概述 五層網絡模型是一種分層的網…

常見的強化學習算法分類及其特點

強化學習&#xff08;Reinforcement Learning, RL&#xff09;是一種機器學習方法&#xff0c;通過智能體&#xff08;Agent&#xff09;與環境&#xff08;Environment&#xff09;的交互來學習如何采取行動以最大化累積獎勵。以下是一些常見的強化學習算法分類及其特點&#…

【LeetCode 熱題 100】438. 找到字符串中所有字母異位詞——(解法三)不定長滑動窗口+數組

Problem: 438. 找到字符串中所有字母異位詞 題目&#xff1a;給定兩個字符串 s 和 p&#xff0c;找到 s 中所有 p 的 異位詞 的子串&#xff0c;返回這些子串的起始索引。不考慮答案輸出的順序。 【LeetCode 熱題 100】438. 找到字符串中所有字母異位詞——&#xff08;解法一&…

求區間最大值

題目描述 給定一個長度為 N 的數列&#xff0c;和 M 次詢問&#xff0c;求出每一次詢問的區間內數字的最大值。 輸入描述 第一行包含兩個整數 N,M&#xff0c;分別表示數列的長度和詢問的個數。 第二行包含 N 個整數&#xff08;記為&#x1d44e;&#x1d456;&#xff09;&am…

調試HDMI音頻能8通道播放聲音

一、使用場景 我們是通過rk主控的hdmi接口播放音視頻給到ite68051芯片解析出8聲道數據,分別通過4路i2s的數據腳給給到fpga去解析 調試步驟: 1.根據相關手冊配置hdmi輸出,hdmi聲卡注冊,如下: hdmi0_sound: hdmi0-sound {status = "disabled";compatible = &qu…

PowerBI 柱狀圖顯示MoM銷量環比示例,以及解決相同列值時設置柱子顏色的問題

先看效果: 假設有Sales表: 1. 我們先給它新增一個計算列&#xff0c;顯示銷售日期的年月 銷售日期YYYYMM YEAR(Sales[銷售日期])*100 MONTH(Sales[銷售日期]) 2. 然后新增一個計算表&#xff0c;用于保存當前最大的銷售日期&#xff0c;和上一個月的日期 DateComparisonT…

【docker】構建時使用宿主機的代理

docker構建過程中報錯: pip 下載失敗 解決辦法:傳遞宿主機的代理 把宿主機的 HTTP_PROXY/HTTPS_PROXY 傳進去,導致容器內的 pip 依然連不上代理,下載 build-dependencies(比如 setuptools)就會失敗。 下面兩步即可解決: Docker 構建階段,127.0.0.1:7890 指向的是 容…

[Java 基礎]算法

什么是算法 程序 數據結構 算法 算法&#xff08;Algorithm&#xff09;就是解決問題的步驟&#xff0c;就像做菜的食譜一樣&#xff0c;告訴計算機一步一步如何完成任務。 例如&#xff1a; 排序算法&#xff1a;把一堆數字從小到大排列搜索算法&#xff1a;在一堆數據里…

C++理解for循環 計算題三

計算a的值 #include <iostream> using namespace std; int main() { int a0;for(int i0;i<3;i){for(int j0;j<3;j){aij;}}cout<<"a的值是 "<<a<<endl; return 0; } 計算a的值 #include <iostream> using namespace std; int …

梳理React中的fiber架構

文章目錄 產生背景核心概念工作原理工作流程優勢特點 產生背景 在React16之前使用的虛擬DOM是數組的形式&#xff0c;又因為React本身是應用級框架&#xff0c;狀態改變后并不能準確知道是哪個組件發生了改變&#xff0c;只能對整個應用進行diff協調&#xff0c;受限于虛擬DOM…

Modbus 數據模型:線圈、寄存器與功能碼詳解(二)

三、Modbus 功能碼詳解 3.1 功能碼分類與作用 Modbus 功能碼是 Modbus 通信協議中的關鍵組成部分&#xff0c;它如同一個 “指令指揮官”&#xff0c;在通信事務處理中扮演著核心角色。功能碼占用 1 個字節的空間&#xff0c;取值范圍為 1 到 255 &#xff08;0x01 - 0xFF&am…

多表連接查詢:語法、注意事項與最佳實踐

&#x1f517; 多表連接查詢&#xff1a;語法、注意事項與最佳實踐 多表連接是 SQL 的核心能力&#xff0c;用于關聯多個表的數據。以下是深度解析&#xff0c;涵蓋語法規范、性能陷阱及實戰技巧&#xff1a; &#x1f4dc; 一、多表連接語法大全 1. 顯式連接&#xff08;推薦…

使用Calibre對GDS進行數據遍歷

在芯片的GDS數據里&#xff0c;使用Calibre對數據進行處理是非常常見的操作&#xff0c;但是GDS是一種和常規設計結構不太一樣的一種數據&#xff0c;這里&#xff0c;通過這個小小的科普文章&#xff0c;一起看看怎么樣在GDS里邊做數據漫游吧&#xff01;閑言少敘&#xff0c;…

PyQtNode Editor 第二篇自定義可視化視圖

在第一篇博客中,我們已經完成了 PyQtNode Editor 的基礎環境搭建,并深入解析了自定義圖形場景QDMGraphicsScene的實現原理。那個帶有網格背景的場景就像一張空白的圖紙,現在我們要在這張圖紙上開始繪制真正的節點系統。 今天我們將聚焦于節點編輯器的核心數據結構設計,實現…

【擴歐應用】同余方程

與擴歐的聯系 在同余方程的求解過程中&#xff0c;我們通常需要將方程轉化為線性不定方程&#xff08;Diophantine 方程&#xff09;的形式&#xff0c;然后使用擴展歐幾里得算法&#xff08;Extended Euclidean Algorithm, EEA&#xff09;求解。 同余方程是怎么轉化為線性不…