AVt天堂网 手机版,亚洲va久久久噜噜噜久久4399,天天综合亚洲色在线精品,亚洲一级Av无码毛片久久精品

當(dāng)前位置:首頁 > 科技  > 軟件

編寫更清晰代碼:去掉所有多余的類型

來源: 責(zé)編: 時(shí)間:2023-10-13 14:34:36 213觀看
導(dǎo)讀最近,在 r/swift 子論壇上,我偶然發(fā)現(xiàn)了一篇介紹“整潔架構(gòu)”項(xiàng)目示例的帖子。這引起了我的興趣,于是我決定在 GitHub 上下載并仔細(xì)研究。帖子截圖初看代碼頗為復(fù)雜,讓我感到迷惑。但在下載和深入研究后,我發(fā)現(xiàn)所有組件都

最近,在 r/swift 子論壇上,我偶然發(fā)現(xiàn)了一篇介紹“整潔架構(gòu)”項(xiàng)目示例的帖子。這引起了我的興趣,于是我決定在 GitHub 上下載并仔細(xì)研究。Vhn28資訊網(wǎng)——每日最新資訊28at.com

帖子截圖帖子截圖Vhn28資訊網(wǎng)——每日最新資訊28at.com

初看代碼頗為復(fù)雜,讓我感到迷惑。但在下載和深入研究后,我發(fā)現(xiàn)所有組件都整合在一起,項(xiàng)目實(shí)現(xiàn)了想要的功能。但我發(fā)現(xiàn)該項(xiàng)目的網(wǎng)絡(luò)模塊的復(fù)雜性較高。僅兩個(gè)簡單的網(wǎng)絡(luò)查詢操作竟涉及如此多的文件,讓人難以理解,讓我頗為驚訝。Vhn28資訊網(wǎng)——每日最新資訊28at.com

因此,我決定對網(wǎng)絡(luò)層進(jìn)行重構(gòu),使其更加模塊化,并對整體組合和用戶界面進(jìn)行了小幅優(yōu)化。為此,我創(chuàng)建了獨(dú)立的項(xiàng)目對原始項(xiàng)目代碼進(jìn)行重構(gòu),你可以在文末找到原始項(xiàng)目和我重構(gòu)后的項(xiàng)目鏈接。Vhn28資訊網(wǎng)——每日最新資訊28at.com

網(wǎng)絡(luò)層——消除嵌套和多余類型

原項(xiàng)目的網(wǎng)絡(luò)層通過協(xié)議和類型結(jié)構(gòu)實(shí)現(xiàn)了高度模塊化,每個(gè)協(xié)議和類型分別負(fù)責(zé)特定功能,大致結(jié)構(gòu)如下:Vhn28資訊網(wǎng)——每日最新資訊28at.com

NetworkManager -> RequestManager -> RequestProtocol -> DataParser -> DataSource -> Repository -> UseCase

上述每一個(gè)類型都承擔(dān)了網(wǎng)絡(luò)過程的一部分職責(zé),例如 DataParser 負(fù)責(zé)數(shù)據(jù)解析,如果想改變數(shù)據(jù)的解析方式,可以通過替換新的 DataParser 來實(shí)現(xiàn),這種組合性是一項(xiàng)優(yōu)點(diǎn)。Vhn28資訊網(wǎng)——每日最新資訊28at.com

但問題在于,由于這些類型相互嵌套,使人難以整體理解,且每個(gè)類型都存于單獨(dú)的文件中。許多通過 Swinject 解析器進(jìn)行注入,這使得整個(gè)網(wǎng)絡(luò)層的工作流程變得難以追蹤。正如 r/swift 中的一名評論者所言,這為代碼增加了一層不必要的“中間層”。Vhn28資訊網(wǎng)——每日最新資訊28at.com

更令人費(fèi)解的是,盡管作者增加了許多協(xié)議和類型來提高代碼的靈活性,但其中存在很多硬編碼的默認(rèn)值。例如,DataParser 被直接編碼在代碼中,而 RequestProtocol.request() 的創(chuàng)建僅通過協(xié)議本身的擴(kuò)展方法來實(shí)現(xiàn)。這種在增加了類型和復(fù)雜性后未充分利用它們的優(yōu)勢的做法,實(shí)在讓人覺得可惜。Vhn28資訊網(wǎng)——每日最新資訊28at.com

為了消除冗余的嵌套以及不必要的類型和協(xié)議,我們可以引入一個(gè)全新的方法:modelFetcher。Vhn28資訊網(wǎng)——每日最新資訊28at.com

static func modelFetcher<T, U: Codable>(    createURLRequest: @escaping (T) throws -> URLRequest,    store: NetworkStore = .urlSession) -> (T) async -> Result<BaseResponseModel<PaginatedResponseModel<U>>, AppError> {    let networkFetcher = selfworkFetcher(store: store)    let mapper: (Data) throws -> BaseResponseModel<PaginatedResponseModel<U>> = jsonMapper()    let fetcher = self.fetcher(        createURLRequest: createURLRequest,        fetch: { request -> (Data, URLResponse) in            try await networkFetcher(request)           }, mapper: { data -> BaseResponseModel<PaginatedResponseModel<U>> in                       try mapper(data)                      })    return { params in          await fetcher(params)         }}

此函數(shù)的設(shè)計(jì)旨在保持與原代碼相同的組合功能,但未采用協(xié)議(protocols)和類型(types),而是通過直接注入操作行為來實(shí)現(xiàn)。需要說明的是,如果這樣更方便,你還可以將其構(gòu)造成一個(gè)帶閉包的結(jié)構(gòu)體,而不僅限于閉包。Vhn28資訊網(wǎng)——每日最新資訊28at.com

接下來,實(shí)際的請求獲取閉包創(chuàng)建過程被大大簡化,唯一會(huì)變化的是請求創(chuàng)建部分。Vhn28資訊網(wǎng)——每日最新資訊28at.com

static func characterFetcher(    store: NetworkStore = .urlSession) -> (CharacterFetchData) async -> Result<BaseResponseModel<PaginatedResponseModel<CharacterModel>>, AppError> {    let createURLRequest = { (data: CharacterFetchData) -> URLRequest in                          var urlParams = ["offset": "/(data.offset)", "limit": "/(APIConstants.defaultLimit)"]                          if let searchKey = data.searchKey {                              urlParams["nameStartsWith"] = searchKey                          }                          return try createRequest(                              requestType: .GET,                              path: "/v1/public/characters",                              urlParams: urlParams                          )                         }    return self.modelFetcher(createURLRequest: createURLRequest)}

優(yōu)化后,我們無需深入到許多不同的文件中,也無需理解眾多的協(xié)議和類型,因?yàn)槲覀兛梢酝ㄟ^直接注入閉包來實(shí)現(xiàn)相同的行為。NetworkStore 負(fù)責(zé)實(shí)際將數(shù)據(jù)發(fā)送到網(wǎng)絡(luò),我們將其傳遞到構(gòu)造函數(shù)中是為了方便后續(xù)的測試模擬(如果有需要的話)。Vhn28資訊網(wǎng)——每日最新資訊28at.com

下面的例子展示了如何通過使用行為替代類型,將原始項(xiàng)目中的協(xié)議和類型進(jìn)行轉(zhuǎn)換:Vhn28資訊網(wǎng)——每日最新資訊28at.com

protocol NetworkManager {    func makeRequest(with requestData: RequestProtocol) async throws -> Data}class DefaultNetworkManager: NetworkManager {    private let urlSession: URLSession    init(urlSession: URLSession = URLSession.shared) {        self.urlSession = urlSession    }    func makeRequest(with requestData: RequestProtocol) async throws -> Data {        let (data, response) = try await urlSession.data(for: requestData.request())        guard let httpResponse = response as? HTTPURLResponse,        httpResponse.statusCode == 200 else { throw NetworkError.invalidServerResponse }        return data    }}

這段代碼還可以繼續(xù)優(yōu)化變得更簡潔:Vhn28資訊網(wǎng)——每日最新資訊28at.com

static func networkFetcher(    store: NetworkStore) -> (URLRequest) async throws -> (Data, URLResponse) {    { request in     let (data, response) = try await store.fetchData(request)     if let httpResponse = response as? HTTPURLResponse,     httpResponse.statusCode != 200 {         throw NetworkError.invalidServerResponse     }     return (data, response)    }}

可以看出,我們在移除類型和協(xié)議的情況下實(shí)現(xiàn)了相同的功能。Vhn28資訊網(wǎng)——每日最新資訊28at.com

另一個(gè)案例是通過函數(shù)創(chuàng)建一個(gè) JSON 映射器,并將其作為閉包返回,保留協(xié)議的靈活性,卻不依賴協(xié)議。例如:Vhn28資訊網(wǎng)——每日最新資訊28at.com

static func jsonMapper<T: Decodable>() -> (Data) throws -> T {    let decoder = JSONDecoder()    decoder.keyDecodingStrategy = .convertFromSnakeCase    return { data in            try decoder.decode(T.self, from: data)           }}

在我看來,與基于協(xié)議/類型的方法相比,這種組合方式讓網(wǎng)絡(luò)層的實(shí)現(xiàn)變得更為直觀和簡潔。Vhn28資訊網(wǎng)——每日最新資訊28at.com

這并不意味著你不應(yīng)使用協(xié)議,但在選擇使用協(xié)議和類型時(shí),應(yīng)明確了解其用途,并思考是否真的需要為每 2-3 行代碼創(chuàng)建一個(gè)完整的類型。Vhn28資訊網(wǎng)——每日最新資訊28at.com

項(xiàng)目模塊劃分

總體上,應(yīng)用程序的模塊劃分還算理想。然而,我覺得可以進(jìn)一步完善項(xiàng)目,方法是對網(wǎng)絡(luò)模塊進(jìn)行明確的劃分。讓我們思考一下:應(yīng)用程序真的需要了解它將使用哪個(gè) JSON 映射器作為網(wǎng)絡(luò)特性嗎?我們是否可以更改網(wǎng)絡(luò)特性的JSON映射器而不破壞整個(gè)結(jié)構(gòu)?如果網(wǎng)絡(luò)模塊能夠自主處理這些內(nèi)容,那就更好了,這樣我們可以專注于使用它的主要目的:獲取超級(jí)英雄數(shù)據(jù)。Vhn28資訊網(wǎng)——每日最新資訊28at.com

我們應(yīng)該限制網(wǎng)絡(luò)模塊接收的內(nèi)容,僅限于有意識(shí)地改變的部分,如用于測試的輸入,而不過多暴露。此外,我們可以只公開實(shí)際使用的部分,例如fetcher功能,而不是整個(gè)NetworkStore模塊的所有底層特性,并將其設(shè)為public。Vhn28資訊網(wǎng)——每日最新資訊28at.com

值得注意的是,網(wǎng)絡(luò)模塊不應(yīng)涉及域的內(nèi)容,最好將ArkanaKeys依賴從整個(gè)項(xiàng)目中獨(dú)立出來,單獨(dú)置于網(wǎng)絡(luò)模塊中。擁有一個(gè)完全隔離的網(wǎng)絡(luò)模塊,可以讓我們在制作任何關(guān)于漫威超級(jí)英雄的應(yīng)用時(shí),輕松地復(fù)用所有的網(wǎng)絡(luò)邏輯。Vhn28資訊網(wǎng)——每日最新資訊28at.com

在提供的示例代碼中,我僅進(jìn)行了“虛擬模塊化”操作,沒有為網(wǎng)絡(luò)模塊創(chuàng)建獨(dú)立的框架,也沒有將ArkanaKeys的依賴關(guān)系轉(zhuǎn)移到那里。相反,我創(chuàng)建了一個(gè)文件夾并加入了訪問控制,模擬了完全獨(dú)立框架的情形。這樣做是為了使演示項(xiàng)目簡潔,實(shí)際上,你只需創(chuàng)建一個(gè)框架并添加到項(xiàng)目中即可。Vhn28資訊網(wǎng)——每日最新資訊28at.com

另一個(gè)更遠(yuǎn)大的目標(biāo)是將 UI 和演示邏輯進(jìn)行分離。目前這兩者相當(dāng)耦合,我覺得這并不是問題。我刪除了 Presentation 文件夾,并把它們和 UI 層放在一起,因?yàn)樵谶@一點(diǎn)上,很難想象使用 HomeViewModel來做除了 HomeView以外的事情,但這是一個(gè)組織代碼的個(gè)人喜好問題。Vhn28資訊網(wǎng)——每日最新資訊28at.com

我最終使用了一個(gè)簡單的 Container類來代替 Swinject,但這也是個(gè)人喜好的問題。無論如何,解析器/容器應(yīng)該避免嘗試解析太多具體的網(wǎng)絡(luò)類型,比如 NetworkManager, DataSource, Repositories和UseCases。在這種情況下,讓我們注入 NetworkStore(我用來替換 NetworkManager的類型)并直接解析UseCase 的依賴。Vhn28資訊網(wǎng)——每日最新資訊28at.com

UI 層的優(yōu)化更新

以下是關(guān)于 UI 層的一些優(yōu)化更新,通過減少縮進(jìn)和刪除 AnyView類型來提高可讀性和性能。將 View從 body中提取出來以提高可讀性,在我看來,盡可能減少縮進(jìn)到只有幾個(gè)級(jí)別是有幫助的。原始應(yīng)用程序在 HomeView中達(dá)到了 13 個(gè)縮進(jìn)級(jí)別!而且,它是應(yīng)用程序的根視圖,所以從一開始就盡可能地使其可讀是一個(gè)好主意。通過將 homeView提取為一個(gè)計(jì)算屬性,我們可以很容易地將縮進(jìn)減少到只有五個(gè)級(jí)別。
示例如下:Vhn28資訊網(wǎng)——每日最新資訊28at.com

public var body: some View {    NavigationStack {        ZStack {            BaseStateView(                viewModel: viewModel,                successView: homeView,                emptyView: BaseStateDefaultEmptyView(),                createErrorView: { errorMessage in                                    BaseStateDefaultErrorView(errorMessage: errorMessage)                                   },                loadingView: BaseStateDefaultLoadingView()            )        }    }    .task {        await viewModel.loadCharacters()    }}

我想最后提一下的是,這個(gè)應(yīng)用使用了一個(gè) BaseStateView,它接受四個(gè)不同的 AnyView來表示應(yīng)用的不同狀態(tài),比如成功、空、錯(cuò)誤等。BaseStateView使用泛型來代替 AnyView會(huì)更合適,因?yàn)?nbsp;AnyView對于 SwiftUI 來說并不總是性能很好。這樣會(huì)提高性能,但是一個(gè)缺點(diǎn)是,它讓我們必須傳入我們想要的具體的 View,比如成功/空/創(chuàng)建/加載,而不是讓它們在構(gòu)造函數(shù)中自動(dòng)為我們完成。
示例如下:Vhn28資訊網(wǎng)——每日最新資訊28at.com

struct BaseStateView<S: View, EM: View, ER: View, L: View>: View {    @ObservedObject var viewModel: ViewModel    let successView: S    let emptyView: EM?    let createErrorView: (_ errorMessage: String?) -> ER?    let loadingView: L?    ...}

為了提高可讀性,你可以使用如SuccessView、EmptyView等名稱。Vhn28資訊網(wǎng)——每日最新資訊28at.com

在 SwiftUI 的上下文中,使用單一基礎(chǔ)控制器/視圖的方法可能不太符合習(xí)慣。與直接將所有這些狀態(tài)處理器添加到基礎(chǔ)視圖上相比,以 ViewModifiers的形式將它們組合起來并添加感覺更為自然。不過,每種方法都有其優(yōu)劣之處。如果你想強(qiáng)調(diào)構(gòu)造函數(shù)的使用,并且想通過減少 ZStacks 的使用來實(shí)現(xiàn),那么這種方法也是可取的。Vhn28資訊網(wǎng)——每日最新資訊28at.com

struct ErrorStateViewModifier<ErrorView: View>: ViewModifier {    @ObservedObject var viewModel: ViewModel    let errorView: (String) -> ErrorView    func body(content: Content) -> some View {        ZStack {            content            if case .error(let message) = viewModel.state {                errorView(message)            }        }    }}

結(jié)論

衷心感謝 mohaned_y98 提供的啟發(fā)和出色的示例項(xiàng)目!本文基于清晰的架構(gòu)原則,采用了與原始項(xiàng)目不同的風(fēng)格進(jìn)行探索。相較于我所重構(gòu)的項(xiàng)目,原始項(xiàng)目有其獨(dú)特的優(yōu)勢,你可根據(jù)項(xiàng)目需求選擇適合的設(shè)計(jì)方案。Vhn28資訊網(wǎng)——每日最新資訊28at.com

在盡量保留初衷的同時(shí),我對項(xiàng)目進(jìn)行了重構(gòu),增強(qiáng)了其人體工程學(xué)和可讀性。鑒于用戶界面或展示層已經(jīng)構(gòu)建得非常穩(wěn)固,我未在這些方面投入過多精力。如果從頭開始,我可能會(huì)選擇不同的編碼方式,但現(xiàn)有的代碼編寫得恰到好處,且運(yùn)作正常。Vhn28資訊網(wǎng)——每日最新資訊28at.com

原始項(xiàng)目和我重構(gòu)后的項(xiàng)目鏈接放在下方,歡迎下載閱讀我重構(gòu)后的項(xiàng)目。你認(rèn)為我忽略了哪些方面?你會(huì)有哪些不同的實(shí)現(xiàn)方法?Vhn28資訊網(wǎng)——每日最新資訊28at.com

原始項(xiàng)目: https://github.com/Mohanedy98/swifty-marvel我重構(gòu)后的項(xiàng)目:https://github.com/terranisaur/Demo-SwiftyMarvelousVhn28資訊網(wǎng)——每日最新資訊28at.com

譯者介紹

劉汪洋,51CTO社區(qū)編輯,昵稱:明明如月,一個(gè)擁有 5 年開發(fā)經(jīng)驗(yàn)的某大廠高級(jí) Java 工程師,擁有多個(gè)主流技術(shù)博客平臺(tái)博客專家稱號(hào)。Vhn28資訊網(wǎng)——每日最新資訊28at.com

原文標(biāo)題:Clean Code Review: Removing All the Extra Types,作者:Alex ThurstonVhn28資訊網(wǎng)——每日最新資訊28at.com

本文鏈接:http://www.tebozhan.com/showinfo-26-13493-0.html編寫更清晰代碼:去掉所有多余的類型

聲明:本網(wǎng)頁內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問題請及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com

上一篇: Tailwind CSS 真有那么好嗎?討厭它的前六大原因

下一篇: 如何更優(yōu)雅的編程?面向接口編程四大法寶!

標(biāo)簽:
  • 熱門焦點(diǎn)
  • 直屏旗艦來了 iQOO 12和K70 Pro同臺(tái)競技

    旗艦機(jī)基本上使用的都是雙曲面屏幕,這就讓很多喜歡直屏的愛好者在苦等一款直屏旗艦,這次,你們等到了。據(jù)博主數(shù)碼閑聊站帶來的最新爆料稱,Redmi下代旗艦K70 Pro和iQOO 12兩款手
  • 6月安卓手機(jī)好評榜:魅族20 Pro蟬聯(lián)冠軍

    性能榜和性價(jià)比榜之后,我們來看最后的安卓手機(jī)好評榜,數(shù)據(jù)來源安兔兔評測,收集時(shí)間2023年6月1日至6月30日,僅限國內(nèi)市場。第一名:魅族20 Pro好評率:95%5月份的時(shí)候魅族20 Pro就是
  • 服務(wù)存儲(chǔ)設(shè)計(jì)模式:Cache-Aside模式

    Cache-Aside模式一種常用的緩存方式,通常是把數(shù)據(jù)從主存儲(chǔ)加載到KV緩存中,加速后續(xù)的訪問。在存在重復(fù)度的場景,Cache-Aside可以提升服務(wù)性能,降低底層存儲(chǔ)的壓力,缺點(diǎn)是緩存和底
  • 如何正確使用:Has和:Nth-Last-Child

    我們可以用CSS檢查,以了解一組元素的數(shù)量是否小于或等于一個(gè)數(shù)字。例如,一個(gè)擁有三個(gè)或更多子項(xiàng)的grid。你可能會(huì)想,為什么需要這樣做呢?在某些情況下,一個(gè)組件或一個(gè)布局可能會(huì)
  • 中國家電海外掘金正當(dāng)時(shí)|出海專題

    作者|吳南南編輯|胡展嘉運(yùn)營|陳佳慧出品|零態(tài)LT(ID:LingTai_LT)2023年,出海市場戰(zhàn)況空前,中國創(chuàng)業(yè)者在海外紛紛摩拳擦掌,以期能夠把中國的商業(yè)模式、創(chuàng)業(yè)理念、戰(zhàn)略打法輸出海外,他們依
  • 消息稱小米汽車開始篩選交付中心:需至少120個(gè)車位

    IT之家 7 月 7 日消息,日前,有微博簡介為“汽車行業(yè)從業(yè)者、長三角一體化擁護(hù)者”的微博用戶 @長三角行健者 發(fā)文表示,據(jù)經(jīng)銷商集團(tuán)反饋,小米汽車目前
  • 華為Mate 60系列用上可變靈動(dòng)島:正式版體驗(yàn)將會(huì)更出色

    這段時(shí)間以來,關(guān)于華為新旗艦的爆料日漸密集。據(jù)此前多方爆料,今年華為將開始恢復(fù)一年雙旗艦戰(zhàn)略,除上半年推出的P60系列外,往年下半年的Mate系列也將
  • 到手價(jià)3099元起!iQOO Neo8 Pro今日首銷:安卓性能最強(qiáng)旗艦

    5月23日,iQOO如期舉行了新品發(fā)布會(huì),全新的iQOO Neo8系列也正式與大家見面,包含iQOO Neo8和iQOO Neo8 Pro兩個(gè)版本,其中標(biāo)準(zhǔn)版搭載高通驍龍8+,而Pro版更
  • 2299元起!iQOO Pad明晚首銷:性能最強(qiáng)天璣平板

    5月23日,iQOO如期舉行了新品發(fā)布會(huì),除了首發(fā)安卓最強(qiáng)旗艦處理器的iQOO Neo8系列新機(jī)外,還在發(fā)布會(huì)上推出了旗下首款平板電腦——iQOO Pad,其最大的賣點(diǎn)
Top