# 定義 當兩個或多個類別/模組互相依賴彼此,形成封閉的迴圈,導致系統無法正確初始化或執行。 # 問題 - 程式無法編譯或執行 - 初始化順序不明,可能導致錯誤 - 結構耦合,難以維護、擴展 --- # 常見類型 1. 模組之間互相引用 2. 類別彼此引用對方 3. 函式之間彼此調用且互相遞迴 --- # 解決方式 1. **重構結構** - 抽共用邏輯到第三方模組 - 改變依賴方向(例如:下層不依賴上層) 2. **延遲依賴(Lazy / Provider)** - 使用 `Provider<T>` 或 `lazy` 等方式延遲注入 - 可有效打破建構期的循環 3. **使用介面或抽象類** - 解耦依賴,透過 interface 隱藏具體實作 4. **依賴注入(Dependency Injection)** - 將依賴從外部提供,而不是內部自行建構 --- # 實務例子:API 呼叫時,而 Token 過期需要刷新時產生的循環依賴 ## 原始依賴鏈 1. Repository 呼叫 ApiService 2. ApiService 經由 Retrofit 使用 OkHttpClient 3. OkHttpClient 裡有 AuthInterceptor(檢查 token) 4. AuthInterceptor 呼叫 TokenRefresher 5. TokenRefresher 呼叫 UserRepository.login() 6. UserRepository 又呼叫 ApiService.login() 7. 回到起點 → 形成循環依賴 --- ## 問題點 - Interceptor 是同步執行,不能直接呼叫 suspend 函式或進行網路請求 - AuthInterceptor 屬於網路層,但呼叫了資料層(UserRepository),層級不對 - ApiService 與 Interceptor 綁定在 OkHttpClient 上,形成建構時的相互依賴 --- ## 解決策略 1. **AuthInterceptor 單純附帶 Token,不做刷新邏輯** 2. **TokenRefresher 使用獨立 Retrofit 實例來刷新 token,避開原本 ApiService** 3. **改用 OkHttp 的 Authenticator 處理 401,自動重新發送請求** 4. **在 safeApiCall 層級統一處理 401,集中處理錯誤與重試邏輯** --- ## 分層原則(Clean Architecture) | 層 | 負責內容 | 範例 | |-----------|-------------------------|------------------------| | Presentation | ViewModel / UI | 呼叫 UseCase | | Domain | UseCase + Interface | TokenRefresher, AuthRepository | | Data | Repository + API + Interceptor | ApiService, UserRepository | | Network | 網路細節 (Retrofit, OkHttp) | AuthInterceptor, Authenticator | --- # 實務應對:將問題轉移到 safeApiCall 中處理 由於循環依賴發生在 Interceptor 介於 Retrofit 與 Dagger 的構建階段之間,處理位置不是那麼容易。 為了解決這個問題,我選擇不在 Interceptor 層級處理 token 過期,而是在上層的 Repository 的位置新增 safeApiCall() 中統一處理錯誤碼(如 401)。 好處: * 可避免 Interceptor 進行網路呼叫造成循環 * 避開 Retrofit/OkHttpClient 建構時的物件初始化衝突 * 可以在資料層處理 token 過期與重試,邏輯集中可控 * 更容易加入 retry、log、回報、fallback 等彈性行為
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up