- Spring Boot進階:原理、實戰與面試題分析
- 鄭天民
- 1636字
- 2022-07-05 09:41:43
2.3.3 消除循環依賴案例分析
在本節中,我們將基于日常開發需求,通過一個具體的案例來介紹組件之間循環依賴的產生過程以及解決方案。
這個案例描述了醫療健康類系統中的一個常見場景,每個用戶都有一份健康檔案,存儲著代表用戶當前健康狀況的健康等級以及一系列的健康任務。用戶每天可以通過完成醫生所指定的任務來獲取一定的健康積分,而這個積分的計算過程取決于該用戶當前的健康等級。也就是說,不同的健康等級下完成同一個任務所能獲取的積分也是不一樣的。反過來,等級的計算也取決于該用戶當前需要完成的任務數量,任務越多說明用戶越不健康,其健康等級也就越低。健康檔案和健康任務之間的關聯關系如圖2-4所示。

圖2-4 健康檔案和健康任務之間的關聯關系
針對這個場景,我們可以抽象出兩個類,一個是代表健康檔案的HealthRecord類,一個是代表健康任務的HealthTask類。我們先來看HealthRecord類,這個類包含一個HealthTask列表以及添加HealthTask的方法,同樣也包含一個獲取健康等級的方法,這個方法根據任務數量來判斷健康等級,如代碼清單2-22所示。
代碼清單2-22 HealthRecord類實現代碼
public class HealthRecord { private List<HealthTask> tasks = new ArrayList<HealthTask>(); public Integer getHealthLevel() { //根據健康任務數量來判斷健康等級 //任務越多說明越不健康,健康等級就越低 if(tasks.size() > 5) { return 1; } if(tasks.size() < 2) { return 3; } return 2; } public void addTask(String taskName, Integer initialHealthPoint) { HealthTask task = new HealthTask(this, taskName, initialHealthPoint); tasks.add(task); } public List<HealthTask> getTasks() { return tasks; } }
對應的HealthTask中顯然應該包含對HealthRecord的引用,同時也實現了一個方法來計算該任務所能獲取的積分,這時候就需要使用到HealthRecord中的等級信息,如代碼清單2-23所示。
代碼清單2-23 HealthTask類實現代碼
public class HealthTask { private HealthRecord record; private String taskName; private Integer initialHealthPoint; public HealthTask(HealthRecord record, String taskName, Integer initialHealthPoint) { this.record = record; this.taskName = taskName; this.initialHealthPoint = initialHealthPoint; } public Integer calculateHealthPointForTask() { //計算該任務所能獲取的積分需要健康等級信息 //健康等級越低積分越高,以鼓勵用戶多做任務 Integer healthPointFromHealthLevel = 12 / record.getHealthLevel(); //最終積分為初始積分加上與健康等級相關的積分 return initialHealthPoint + healthPointFromHealthLevel; } public String getTaskName() { return taskName; } public int getInitialHealthPoint() { return initialHealthPoint; } }
從代碼中,我們不難看出HealthRecord和HealthTask之間存在明顯的相互依賴關系。
那么,如何消除循環依賴?軟件行業有一句很經典的話,即當我們碰到問題無從下手時,不妨考慮一下是否可以通過“加一層”的方法進行解決。消除循環依賴的基本思路也是這樣,就是通過在兩個相互循環依賴的組件之間添加中間層,變循環依賴為間接依賴。有三種方法可以做到這一點,分別是提取中介者、轉移業務邏輯和引入回調。
1. 提取中介者
我們先來看第一種方法:提取中介者。提取中介者的核心思想是把兩個相互依賴的組件中的交互部分抽象出來形成一個新的組件,而新組件同時包含著對原有兩個組件的引用,這樣就把循環依賴關系剝離出來并提取到一個專門的中介者組件中,如圖2-5所示。

圖2-5 提取中介者之后的類圖
這個中介者組件的實現也非常簡單,通過提供一個計算積分的方法來對循環依賴進行了剝離,該方法同時依賴于HealthRecord和HealthTask對象,并實現了原有HealthTask中根據HealthRecord的等級信息進行積分計算的業務邏輯。中介者HealthPointMediator類的實現代碼如代碼清單2-24所示。
代碼清單2-24 HealthPointMediator類實現代碼
public class HealthPointMediator { private HealthRecord record; public HealthPointMediator(HealthRecord record) { this.record = record; } public Integer calculateHealthPointForTask(HealthTask task) { Integer healthLevel = record.getHealthLevel(); Integer initialHealthPoint = task.getInitialHealthPoint(); Integer healthPoint = 12 / healthLevel + initialHealthPoint; return healthPoint; } }
2. 轉移業務邏輯
我們繼續介紹第二種消除循環依賴的方法,這就是轉移業務邏輯。這種方法的實現思路在于提取一個專門的業務組件來完成對等級的計算過程。這樣,HealthTask原本對HealthRecord的依賴就轉移到了對這個業務組件的依賴,而這個業務組件本身不需要依賴任何對象,如圖2-6所示。

圖2-6 轉移業務邏輯之后的類圖
圖2-6中的專門負責處理業務邏輯的HealthLevelHandler類的實現代碼也很簡單,如代碼清單2-25所示。
代碼清單2-25 HealthLevelHandler類實現代碼
public class HealthLevelHandler { private Integer taskCount; public HealthLevelHandler(Integer taskCount) { this.taskCount = taskCount; } public Integer getHealthLevel() { if(taskCount > 5) { return 1; } if(taskCount < 2) { return 3; } return 2; } }
3. 引入回調
介紹完了提取中介者和轉移業務邏輯這兩種方法之后,我們來看最后一種消除循環依賴的方法,這種方法會采用回調接口。所謂回調,本質上就是一種雙向調用模式,也就是說,被調用方在被調用的同時也會調用對方。在實現上,我們可以提取一個用于計算健康等級的業務接口,然后讓HealthRecord去實現這個接口。我們同樣將這個接口命名為HealthLevelHandler,其中包含一個計算健康等級的方法定義。這樣,HealthTask在計算積分時只需要依賴這個業務接口,而不需要關心這個接口的具體實現類,如圖2-7所示。

圖2-7 引入回調之后的類圖
由于篇幅有限,完整的消除循環依賴案例代碼可以在以下GitHub地址下載:https://github.com/tianminzheng/acyclic-relationships-demo。
- 零基礎搭建量化投資系統:以Python為工具
- SQL Server 2012數據庫技術及應用(微課版·第5版)
- Hands-On Enterprise Automation with Python.
- Python編程從0到1(視頻教學版)
- 低代碼平臺開發實踐:基于React
- Android應用案例開發大全(第二版)
- Spring Boot實戰
- Processing創意編程指南
- C# Multithreaded and Parallel Programming
- Zabbix Performance Tuning
- Beginning C# 7 Hands-On:The Core Language
- 基于JavaScript的WebGIS開發
- ASP.NET Core 2 High Performance(Second Edition)
- TypeScript High Performance
- 交互設計語言:與萬物對話的藝術(全兩冊)