網站首頁 編程語言 正文
背景現狀
項目每積累到一定程度,代碼的重構優化是必經之路。
試卷項目初期,整體錯誤Code較少,直接使用更便于處理錯誤狀態,因此便全部歸整到一個單獨的 NetWorkError.ResponseCodeType
中,但是隨著項目功能的豐富,各個功能模塊越來越多,模塊錯誤的處理也各不相同,每個模塊都關聯了所有的錯誤Code,后續還會持續增長,導致越來越難以維護。
enum ResponseCodeType: Int { case success = 0 case tokenExpire = 11001 case overVerifyCode = 11011 case verifyCodeExpire = 11002 case verifyCodeIncorrect = 11003 case autoLoginFailed = 11004 case appidLoginFailed = 11005 case phoneIsRegisted = 11006 case phoneHasBinded = 11010 case joinedBeePlan = 11100002 case uploadRepeate = 11020005 case wechatHasBinded = 11010017 case phoneHasBindedOtherWeChat = 11010022 case todayIsSignIned = 11140003 case subjectCountLimit = 11150004 case invalidTagName = 11160002 case alreadyExistsTagName = 11160003 case outOfMaxTagsCount = 11160004 case notRegisterHomework = 11010033 case notSupportNumber = 11010028 case wrongTeamCode = 11210005 case classNotFound = 11210006 case nicknameExists = 11210007 case joinClassThreeTimes = 11210008 case identityNickNameExists = 11210014 case checkClassCodeMax = 11210016 case createClassMAx = 11210015 case joinTeamMax = 11210017 case studentCountMax = 11210018 case other = -99999 }
問題分析
提前分析、明確目標。
期望結果
- 錯誤處理分為兩部分:通用、自定義模塊,二者各自處理
- 拓展性強,各個模塊可自定義并處理錯誤,基類代碼保持穩定不變
- 支持點語法、窮舉校驗,使用清晰便捷
技術選型
根據期望結果,可以大致選定技術方向
- 拓展性:泛型、協議
- 類型窮舉:枚舉
優化解決
前后對比,不斷調優。
Error模型
- 區分通用和自定義模塊
- 將 ResponseCodeType 降為通用Code類型,可以將其類型固定
- 替換 NetWorkError,使用 ModuleRespError 作為基類Error,通過泛型為外部模塊提供自定義能力
優化前
struct NetWorkError: Error { var code: ResponseCodeType = .other var msg: String { code.errorString } }
優化后
/// 錯誤類型描述 public protocol ISErrorProtocol { var errorString: String { get } } public enum ModuleRespError<T: ISErrorProtocol>: Error { /// 對應模塊自定義類型code case type(_ value: T) /// 基類請求code case baseType(_ value: ResponseCodeType) /// 錯誤提示歸整 public var mapErrorString: String { switch self { case .type(let value): return value.errorString case .baseType(let value): return value.errorString } } }
基類Request
使用協議的類型占位符 associatedtype,便于后續進行 rawValue 的枚舉映射
- 分層處理錯誤類型,基類錯誤放到基類請求的回調中處理,拋出模塊的錯誤code
在ISTargetType協議中關聯錯誤碼類型 associatedtype ErrorCodeType: RawRepresentable
public protocol ISTargetType { /// 錯誤碼類型,由各模塊自定義 associatedtype ErrorCodeType: RawRepresentable }
優化前
/// 根據 ISTargetType 枚舉類型調用接口,返回 model static func requestISType<T: ISTargetType>(_ server: T, completion: @escaping (_ model: NetworkModelResponse?, _ code: ResponseCodeType) -> Void) { // ... Network.IS.fetchDataDic(server) { dataDic in guard let dataDic = dataDic, let model: NetWorkResponseModel = NetWorkResponseModel.deserialize(from: dataDic) else { completion(nil, .other) return } // 判斷code 是否為token過期 let codeValue = model.ret ?? ResponseCodeType.other.rawValue // errorType let codeType = ResponseCodeType(rawValue: codeValue) ?? .other // 基類Code處理,token過期 NetWorkRequest.checkTokenDidExpire(codeType) // 拋出的code:基類、模塊混在一起 completion(model, codeType) } }
優化后
/// T.ErrorCodeType: 遵循 RawRepresentable 協議的泛型 /// Result<Success, Failure> 拆分成功、失敗邏輯 static func requestISResultType<T: ISTargetType>(_ server: T, result: @escaping ((Result<NetWorkResponseModel, ModuleRespError<T.ErrorCodeType>>) -> Void)) { // ... Network.IS.fetchDataDic(server) { dataDic in // 接口數據處理 guard let dataDic = dataDic, let model: NetWorkResponseModel = NetWorkResponseModel.deserialize(from: dataDic), let retCode = model.ret else { // 接口錯誤,默認基類錯誤 let error: ModuleRespError<T.ErrorCodeType> = .baseType(.other) result(.failure(error)) return } if retCode == 0 { // 成功返回 result(.success(model)) return } // 請求失敗 if let baseType = ResponseCodeType(rawValue: retCode) { result(.failure(.baseType(baseType))) // 優先處理基類錯誤code,例如 token失效 NetWorkRequest.checkTokenDidExpire(baseType) } else if let retValue = retCode as? T.ErrorCodeType.RawValue, let moduleType = T.ErrorCodeType(rawValue: retValue) { // 解析并返回模塊錯誤碼 result(.failure(.type(moduleType))) } } }
模塊調用
- 各模塊自定義ErrorCode,互不干涉
- 通過泛型參數定義ErrorCode類型
- 使用Result<Success, Failure>,消除結果可選值,成功失敗二選一,區分處理
- 限制失敗Error類型,僅需處理當前模塊和基礎錯誤,無需關注其他類型錯誤
優化前
public func queryDemo(with params: [String: String], completionHandler: @escaping (_ model: DemoModel?, _ code: ResponseCodeType) -> Void) { NetWorkRequest.requestISType(GroupQueryServer.createGroup(params)) { model in // ... let code = model.ret ?? -1 let type = ResponseCodeType(rawValue: code) ?? .other guard type == .success, let result = DemoModel.deserialize(from: model.data) else { completionHandler(nil, type) return } completionHandler(.success(resultModel)) } }
logic.queryDemo(with: params) { model, code in // 只能通過解包model來判斷接口的成功或失敗 guard let model = model else { // 失敗處理 handleFail(code: code) return } // 成功處理 hanldeSuccess() } private func handleFail(code: ResponseCodeType) { // ... // 當前模塊錯誤處理 let showWarning = code == .wrongTeamCode || code == .classNotFound // UI處理 warningLabel.isHidden = !showWarning // 提示 CEProgressHUD.showTextHUD(code.errorString) }
優化后
public enum StudyGroupRespCode: Int, ISErrorProtocol { case wrongTeamCode = 11210005 case classNotFound = 11210006 case nicknameExists = 11210007 case joinClassThreeTimes = 11210008 case identityNickNameExists = 11210014 case checkClassCodeMax = 11210016 case createClassMAx = 11210015 case joinTeamMax = 11210017 case studentCountMax = 11210018 case folderLevelLimit = 11210027 case curIdentifierError = 11210011 case clockFrequencyInvalid = 11210036 case other }
public func queryDemo(with params: [String: String], completionHandler: @escaping ((Result<ClassItemModel, ModuleRespError<StudyGroupRespCode>>) -> Void)) { // 基類請求 NetWorkRequest.requestISResultType(GroupQueryServer.createGroup(params)) { result in switch result { case .success(let success): // 結果處理que if let resultModel = ClassItemModel.deserialize(from: success.data) { // 轉換模塊模型model completionHandler(.success(resultModel)) } else { // 轉化失敗,默認other completionHandler(.failure(.type(.other))) } case .failure(let error): // 拋出的模塊錯誤 completionHandler(.failure(error)) } }
logic.queryDemo(with: params) { result in // 通過 Result 劃分結果狀態 switch result { case .success(let model): // 成功處理 hanldeSuccess() case .failure(let error): // 失敗處理 handleError(error) } } // 示例為簡單處理,若需精細化處理錯誤,拆分優化后的代碼,邏輯明顯更加清晰 private func handleError(_ error: ModuleRespError<StudyGroupRespCode>) { switch error { case .type(let code): // ... // 當前模塊錯誤處理 let showWarning = code == .wrongTeamCode || code == .classNotFound // UI處理 warningLabel.isHidden = !showWarning // 提示 CEProgressHUD.showTextHUD(code.errorString) case .baseType(let error): // 基類錯誤處理 CEProgressHUD.showTextHUD(error.errorString) } }
總結
至此,我們已經了解了有關ErrorCode的重構優化的大體邏輯,從后續的開發流程結果可以看出,確實對項目的Code混亂增長有了良好的控制,各模塊只需要關注處理自己的異常code,降低了維護代碼難度,后續也會持續關注和優化。
參考資料
- Result 還是 Result<T, E: Error>
原文鏈接:https://onevcat.com/2018/10/swift-result-error/
相關推薦
- 2022-06-29 Python容器類型轉換的3種方法實例_python
- 2022-12-13 OpenCV視頻流C++多線程處理方法詳細分析_C 語言
- 2022-11-03 C#如何給新建的winform程序添加資源文件夾Resources_C#教程
- 2022-05-28 基于ASP.NET實現驗證碼生成詳解_實用技巧
- 2022-06-25 關于Ubuntu?Server?18.04?LTS?安裝Tomcat并配置systemctl管理To
- 2021-10-28 C++文件流讀寫操作詳解_C 語言
- 2022-08-20 Python數據讀寫之Python讀寫CSV文件_python
- 2022-10-03 Python使用Rich?type和TinyDB構建聯系人通訊錄_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支