国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Day962.如何更好地重構和組織后端代碼 -遺留系統(tǒng)現(xiàn)代化實戰(zhàn)

這篇具有很好參考價值的文章主要介紹了Day962.如何更好地重構和組織后端代碼 -遺留系統(tǒng)現(xiàn)代化實戰(zhàn)。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

如何更好地重構和組織后端代碼

Hi,我是阿昌,今天學習記錄是關于如何更好地重構和組織后端代碼的內(nèi)容。

如果說在氣泡上下文中開發(fā)新的需求,類似于老城區(qū)旁邊建設一個新城區(qū),那么在遺留系統(tǒng)中開發(fā)新的需求,就類似于在老城區(qū)內(nèi)部開發(fā)新的樓盤。

這就必然要涉及到拆遷的問題。拆遷終歸是一個聲勢浩大的工程,居民要先搬到別的地方,再拆除舊的建筑,蓋起新的樓宇,一番折騰之后,老居民才能搬進新家。

不過軟件的好處就在于它是“軟”的,不需要這么費勁兒。

可以很容易地復制、刪除和添加新的代碼,輕松地實現(xiàn)一個架構的變遷。


一、修繕者模式

絞殺植物模式適合于用新的系統(tǒng)和服務,替換舊的系統(tǒng)或舊系統(tǒng)中的一個模塊。

在舊系統(tǒng)內(nèi)部,也可以使用類似的思想來替換一個模塊,只不過這個模塊仍然位于舊系統(tǒng)中,而不是外部。把這種方式叫做 修繕者模式。

Day962.如何更好地重構和組織后端代碼 -遺留系統(tǒng)現(xiàn)代化實戰(zhàn)

在修繕時,通過開關隔離舊系統(tǒng)待修繕的部分,并采用新的方式修改。

在修繕的過程中,模塊仍然能通過開關對外提供完整功能。

這就好比是在老城區(qū)中修路,如果斷路施工對交通的影響就太大了。

更常見的做法是修繕其中的半條路,留另外半條來維持交通。不過,這必然會造成一定的擁堵。但在軟件中就好辦多了,可以將道路(待修繕的模塊)“復制”出來一份,以保障通行正常。等原道路修繕好之后,再刪除掉復制出來的道路即可。

用修繕者模式去修復過一個性能問題。一個 API 的請求特別慢,在本地修好后,在生產(chǎn)環(huán)境改觀不大。

推測這應該是數(shù)據(jù)分布導致的問題,本地環(huán)境的數(shù)據(jù)分布無法準確模擬生產(chǎn)環(huán)境。但當時的安全策略不允許訪問生產(chǎn)數(shù)據(jù)庫。

于是,接下來做調(diào)優(yōu)時,并沒有直接修改這個 API,而是將 API 復制了一份出來,一個用來維持老的功能,一個用來性能調(diào)優(yōu)。

同時添加了一個針對這個 API 的 Filter,根據(jù)開關來決定要調(diào)用哪個 API。通過收集調(diào)優(yōu) API 中的日志,不斷地優(yōu)化,直到解決性能問題。

這時再清理掉舊 API、Filter 和開關。這樣做的好處是,由于你無法預測修繕過程中會產(chǎn)生哪些問題,這種通過開關保留回退余地的方法,顯然是更靈活的。

如何實現(xiàn)前端的增量演進和隨時回退,其實也是這種修繕者模式的思想。

將所有要修改的頁面復制出來一份,然后再加入開關,就可以放心地重構頁面了。

在沒有單元測試的情況下,通過修繕者的方式來重構的。把代碼復制出來,重構完之后,通過開關在調(diào)用端切換,以完成 A/B 測試,從而實現(xiàn)安全地重構。

// 舊方法
public List<int[]> getThem() {
 List<int[]> list1 = new ArrayList<int[]>();
 for (int[] x : theList)
   if (x[0] == 4)
    list1.add(x);
 return list1;
}
// 新方法
public List<Cell> getFlaggedCells()  {
  return gameBoard.stream().filter(c -> c.isFlagged()).collect(toList());
}
// 調(diào)用端
List<int[]> cells;
List<Cell> cellsRefactored;
if (toggleOff) {
  cells = getThem();
  // 其他代碼
}
else {
  cellsRefactored = getFlaggedCells();
  // 其他代碼
}

二、抽象分支

這種優(yōu)雅的方式就是,把要重構的方法重構成一個方法對象,然后提取出一個接口,待重構的方法是接口的一個實現(xiàn),重構后的方法是另一個實現(xiàn)。按這種方式重構之后的代碼如下所示:

public interface CellsProvider {
  List<int[]> getCells();
}

public class OldCellsProvider implements CellsProvider {
  @Override
  public List<int[]> getCells() {
    List<int[]> list1 = new ArrayList<int[]>();
    for (int[] x : theList)
      if (x[0] == 4)
        list1.add(x);
    return list1;
  }
}
public class NewCellsProvider implements CellsProvider {
  @Override
  public List<int[]> getCells() {
    return gameBoard.stream().filter(c -> c.isFlagged()).map(c -> c.getArray()).collect(toList());
  }
}

在調(diào)用端,只需要通過工廠模式,來根據(jù)開關得到 CellIndexesProvider 的不同實現(xiàn),其余的代碼都保持不變。在通過 A/B 測試之后,再刪除舊的實現(xiàn)和開關。

這種方法不但可以進行安全地重構,還可以用新的實現(xiàn)替換舊的實現(xiàn),完成功能或技術的升級。把這種模式叫做抽象分支(Branch by Absctration)。

當進行大的技術改動時,通常需要花費較長的時間。比如用 MyBatis 替換 Hibernate,或用 Kafka 替換 RabbitMQ。

傳統(tǒng)的做法是,在當前的產(chǎn)品代碼分支上創(chuàng)建一個新的分支,大規(guī)模去重寫。

這個分支發(fā)布之前要經(jīng)歷很長一段時間,直到最后全部修改完成后,才能把分支合并到產(chǎn)品代碼分支上。

更糟糕的是,這樣做合并時的代碼沖突會非常嚴重,而且架構調(diào)整后,首次上線大概率會出問題,交付風險非常高,無法做到增量演進。

為了解決這樣的問題,Martin Fowler 提出了抽象分支模式。

可以在不創(chuàng)建真實分支的情況下,通過技術手段,將大的重構項目分解成多個小步驟,每個小步驟都不會破壞功能,都是可以交付的,這樣就可以逐步完成架構的調(diào)整。

Day962.如何更好地重構和組織后端代碼 -遺留系統(tǒng)現(xiàn)代化實戰(zhàn)

它的基本步驟是這樣的。先為舊實現(xiàn)創(chuàng)建一個抽象層,讓舊的模塊去實現(xiàn)這個抽象層。

注意,這里的抽象層并不一定是接口,有可能是一系列接口或抽象類。

然后,讓部分調(diào)用端代碼依賴這個抽象層,而不是舊的模塊。

同樣要注意,這個替換是逐步進行的,不是一次性全部替換掉。

等全部調(diào)用端都依賴抽象層后,開始編寫新的實現(xiàn),并讓部分模塊使用新的實現(xiàn)。

這個過程也是逐步進行的,一方面可以更好地驗證新實現(xiàn),另一方面也可以隨時回退。

當全部調(diào)用端都使用新的實現(xiàn)后,再刪除舊的實現(xiàn)。

有的時候你需要讓新舊實現(xiàn)同時存在,對不同的調(diào)用端提供不同的實現(xiàn),這也是很常見的情況。

由于新代碼一直可以工作,因此你可以不斷提交、不斷交付、不斷驗證。

在實際工作中,抽象分支的運用還是非常廣泛的。一個技術改動,在初始化 Redis 的時候,改為從配置文件中讀取密碼,而不是從數(shù)據(jù)庫中讀取密碼。

對于這樣一個替換,可能直接三下五除二就完成了,但領悟了抽象分支之后,發(fā)現(xiàn)可以用更加優(yōu)雅的方式實現(xiàn)這個替換。一篇博客,可以當做加餐。


三、擴張與收縮模式

有的時候要修改的是接口本身(這里的接口是指方法的參數(shù)和返回值),這時候就不太容易通過抽象分支去替換了。

以前返回的是 List,而現(xiàn)在想打破這個接口,返回 List。

因為 List 仍然存在嚴重的基本類型偏執(zhí)的壞味道,而且本來已經(jīng)提取了 Cell 類,又通過 getArray 返回數(shù)組,簡直是多此一舉。

這時可以使用擴張 - 收縮(expand-contract)模式,也叫并行修改(Parallel Change)模式。它一般包含三個步驟,即擴張、遷移和收縮。

這里的擴張是指建立新的接口,它相比原來舊的代碼新增了一些東西,因此叫做“擴張”;而收縮是指刪除舊的接口,它比之前減少了一些東西,因此叫“收縮”。

一般來說,它會在類的內(nèi)部新建一些方法,以提供新的接口(即擴張),然后再逐步讓調(diào)用端使用新的接口(即遷移),當所有調(diào)用端都使用新的接口后,就刪除舊的接口(即收縮)。

拿剛才這個例子來說,提取完方法對象后的代碼如下所示:

public class CellsProvider {
  public List<int[]> getCells() {
    List<int[]> list1 = new ArrayList<int[]>();
    for (int[] x : theList)
      if (x[0] == 4)
        list1.add(x);
    return list1;
  }
}

可以在這個方法對象中進行擴張新增一個方法,以提供不同的接口:

public class CellsProvider {
  public List<int[]> getCells() {
    // 舊方法
  }
  public List<Cell> getFlaggedCells() {
    return theList.stream().filter(c -> c.isFlagged()).collect(toList());
  }
}

然后,讓調(diào)用端都調(diào)用這個新的 getFlaggedCells 方法,而不是舊的 getCells 方法。

在替換的過程中,新老方法是同時存在的,這也是為什么這個模式也叫并行修改。

等所有調(diào)用端都修改完畢,就可以刪掉舊方法了。

Day962.如何更好地重構和組織后端代碼 -遺留系統(tǒng)現(xiàn)代化實戰(zhàn)

在老城區(qū)改造的過程中,這種擴張與收縮模式也是很常見的。城市完成了一次取暖線路改造,從以前的小區(qū)鍋爐房供暖改成了全市的熱力供暖。

施工方并沒有將小區(qū)內(nèi)舊的供暖管道直接連到市政熱力的管線上,而是在舊的管線旁邊新鋪了一條管線(即擴張),連接到市政管線。

在供暖期,兩條管線是并行運行的,一旦新管線發(fā)生問題,可以很快地切回舊的小區(qū)供暖。等并行運行一段時間后,判斷新管線沒問題了,再重新挖溝,拆除舊管線(即收縮)。

有的時候市民不理解為什么天天挖坑,但實際上這么做,都是為了保障供暖的安全性和高可用性啊。


四、再談接縫

在抽象分支中,我們提取的接口其實是一個接縫。

沒錯,接縫不但可以用來在測試中替換已有的實現(xiàn),它本身其實也是一個業(yè)務變化的方向。

在開發(fā)過程中,需要時刻去關注接縫,關注這種可能會產(chǎn)生變化的地方。

比如項目中使用了 RabbitMQ 作為消息中間件,發(fā)送和接受消息的代碼和 RabbitMQ 的 SDK 緊密耦合,這會帶來兩方面隱患,一方面當你想替換 MQ 的時候,需要修改全部調(diào)用點,另一方面,它也不好寫測試。

當意識到它其實是一個接縫的時候,就可以很輕松地通過一系列接口來隔離 SDK。

當需要替換 MQ 的時候,只需要提供一套新的實現(xiàn)類。這時的實現(xiàn)類應該叫做適配器(Adaptor),它其實也起到了防腐層的作用。而在單元測試中,可以通過測試替身構建一組 Fake 的實現(xiàn)類,以提供內(nèi)存中的 MQ 功能。這樣的方案,既優(yōu)雅又靈活。除了代碼中蘊含著很多接縫,架構中也存在接縫。

延續(xù)上面 MQ 替換的例子,因為有很多在途的消息還沒有處理,這種技術遷移很難做到不停機地絲滑切換。

這時可以利用這個架構接縫,使用事件攔截模式,將發(fā)往 RabbitMQ 中的消息也同步發(fā)給新的 MQ(比如 Kafaka)。

同時,消費端可以通過冪等 API,來消除重復消費造成的問題。這樣一來,系統(tǒng)中就有兩個消息中間件同時存在,同時提供消息機制。

當基礎設施搭建好之后,就可以實現(xiàn)新老 MQ 的無縫切換了。


五、總結

  • 修繕者模式和絞殺植物類似,可以用來改善單體內(nèi)的某個模塊。
  • 抽象分支模式可以通過一個抽象,優(yōu)雅地替換舊的實現(xiàn)。
  • 擴張收縮模式主要用于接口無法向后兼容的情況,一張一縮,一個接口就改造完了。
  • 同時,除了代碼中的接縫,架構中也存在接縫,可以利用它們來實現(xiàn)架構中的替換。

無論是絞殺植物、修繕者、抽象分支還是擴張收縮,它們在實施的過程中,都允許新舊實現(xiàn)并存,這種思想叫做并行運行(Parallel Run)。這是貫徹增量演進原則的基本思想,希望能牢牢記住。

說的絞殺植物、氣泡上下文、修繕者、抽象分支、擴張收縮、并行運行等模式,其實概念上都差不多,之所以叫不同的名字,是因為它們解決的是不同的問題。

比如絞殺植物模式解決的是新老系統(tǒng)的替換,修繕者模式解決的是一個服務內(nèi)部模塊的替換,而氣泡上下文專門用于將新需求和老系統(tǒng)隔離開來。

Day962.如何更好地重構和組織后端代碼 -遺留系統(tǒng)現(xiàn)代化實戰(zhàn)

這就像不同的設計模式雖然叫不同的名字,但構造型模式用來解決不同場景下的對象構造,行為型模式用來處理不同場景下的行為選擇。

必須深刻理解這些模式,才能做出正確的選擇。

最后,王健對于各種模式的高度抽象,他的十六字心法如余音繞梁,三日不絕。

舊的不變,新的創(chuàng)建,一步切換,舊的,再見。文章來源地址http://www.zghlxwxcb.cn/news/detail-434777.html


到了這里,關于Day962.如何更好地重構和組織后端代碼 -遺留系統(tǒng)現(xiàn)代化實戰(zhàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • Day967.團隊拓撲學 -遺留系統(tǒng)現(xiàn)代化實戰(zhàn)

    Day967.團隊拓撲學 -遺留系統(tǒng)現(xiàn)代化實戰(zhàn)

    Hi,我是 阿昌 ,今天學習記錄的是關于 團隊拓撲學 的內(nèi)容。 看看最近這幾年來新誕生的組織結構模型—— 團隊拓撲學 (Team Topologies)。 盡管組件團隊、特性團隊和 Spotify 模型,都為團隊的組成提供了不錯的建議,但團隊的類型應該是什么樣并沒有一致的標準。 如果所有

    2024年02月06日
    瀏覽(17)
  • Day960.架構現(xiàn)代化-微服務 -遺留系統(tǒng)現(xiàn)代化實戰(zhàn)

    Day960.架構現(xiàn)代化-微服務 -遺留系統(tǒng)現(xiàn)代化實戰(zhàn)

    Hi,我是 阿昌 ,今天學習記錄的是關于 架構現(xiàn)代化-微服務 的內(nèi)容。 在 自治氣泡模式 的基礎上,通過 事件攔截 來實現(xiàn) 數(shù)據(jù)同步 ,給氣泡和遺留系統(tǒng)之間又加上 API 訪問 這個通信渠道。 這時的自治氣泡就和真正的微服務差不多了。 有了這種模式,在開發(fā)一個全新的需求時

    2024年02月02日
    瀏覽(156)
  • 程序員如何高質(zhì)量重構代碼?

    程序員如何高質(zhì)量重構代碼?

    有道無術,術尚可求也,有術無道止于術。你好,我是程序員雪球,今天和你聊聊程序員重構代碼那些事。 ? 程序員重構代碼的重要性不言而喻,但如何進行有效的重構呢?下面是一些建議和指導。 為什么要重構? 重構是提高代碼質(zhì)量和可維護性的重要手段,旨在在不改變

    2024年02月15日
    瀏覽(43)
  • Go代碼包與引入:如何有效組織您的項目

    Go代碼包與引入:如何有效組織您的項目

    本文深入探討了Go語言中的代碼包和包引入機制,從基礎概念到高級應用一一剖析。文章詳細講解了如何創(chuàng)建、組織和管理代碼包,以及包引入的多種使用場景和最佳實踐。通過閱讀本文,開發(fā)者將獲得全面而深入的理解,進一步提升Go開發(fā)的效率和質(zhì)量。 關注公眾號【Tech

    2024年02月09日
    瀏覽(95)
  • 【Java設計模式 規(guī)范與重構】 二 重構的保障:單元測試,以及如何提高代碼可測試性

    其實之前的工作中強調(diào)過很多次自己做測試的重要性,例如講單元測試的: 【C#編程最佳實踐 一】單元測試實踐 ,講單元測試規(guī)范的 【阿里巴巴Java編程規(guī)范學習 四】Java質(zhì)量安全規(guī)約 ,講接口測試的: 【C#編程最佳實踐 十三】接口測試實踐 ,這里舊事重提就不再詳細展開

    2023年04月25日
    瀏覽(136)
  • 如何更好的與ChatGPT人機對話進行輔助pyhton代碼開發(fā)

    在開始之前,你需要安裝Python和OpenAI API的Python客戶端。安裝方法可以參考OpenAI官方文檔。在安裝完成之后,你需要設置OpenAI API密鑰,以便與ChatGPT進行通信。你可以在OpenAI的網(wǎng)站上注冊并創(chuàng)建API密鑰。 在安裝和設置完成之后,你可以開始與ChatGPT對話了??梢允褂肞ython的Open

    2023年04月21日
    瀏覽(27)
  • 虹科分享|您的遺留系統(tǒng)的安全性如何?

    虹科分享|您的遺留系統(tǒng)的安全性如何?

    自2023年1月10日起,Windows 7、Windows 8、Windows 8.1及其衍生產(chǎn)品Windows Embedded以及Windows Server 2008 R2將不再收到微軟提供的補丁程序。數(shù)以百萬計的設備現(xiàn)在將成為“遺留”設備,并產(chǎn)生一系列新的遺留安全風險。 Windows 7支持結束,8/8.1被切斷 微軟的2023年1月發(fā)行說明包括了針對微

    2024年02月05日
    瀏覽(21)
  • 【設計模式之美】重構(三)之解耦方法論:如何通過封裝、抽象、模塊化、中間層等解耦代碼?

    【設計模式之美】重構(三)之解耦方法論:如何通過封裝、抽象、模塊化、中間層等解耦代碼?

    重構可以分為大規(guī)模高層重構(簡稱“大型重構”)和小規(guī)模低層次重構(簡稱“小型重構”)。 通過解耦對代碼重構,就是保證代碼不至于復雜到無法控制的有效手段。 ? 代碼是否需要“解耦”? 看修改代碼會不會牽一發(fā)而動全身。 依賴關系是否復雜 把模塊與模塊之間

    2024年01月16日
    瀏覽(27)
  • day3 ARM寄存器組織

    day3 ARM寄存器組織

    目錄 寄存器 ?ARM寄存器 專用寄存器 CPSR寄存器 概念: 寄存器是處理器內(nèi)部的存儲器,沒有地址; 作用: 一般用于暫時存放參與運算的數(shù)據(jù)和運算結果; 分類: 包括通用寄存器、專用寄存器、控制寄存器; ? 注:在某個特定模式下只能使用當前模式下寄存器,一個模式下

    2024年02月08日
    瀏覽(28)
  • Day944.度量指標 -系統(tǒng)重構實戰(zhàn)

    Day944.度量指標 -系統(tǒng)重構實戰(zhàn)

    Hi,我是 阿昌 ,今天學習記錄的是關于 度量指標 的內(nèi)容。 很多時候在研發(fā)過程中,都習慣性地用“拍腦袋”的方式來看待一個事情。例如這個代碼寫得不好、這個自動化測試覆蓋不充分、版本的發(fā)布頻率太差了等等。往往只知道哪里有問題,但是卻不知如何去找出根因,真

    2024年02月06日
    瀏覽(26)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包