我是如何將業務代碼寫優雅的

0x00 前言

我是一名來自螞蟻金服-保險事業羣的前端工程師,在一線大廠的業務部門寫代碼,很是辛苦但也很是充實。業務代碼不一樣於框架代碼、我的項目或者開源項目,它的特色在於邏輯複雜、先後依賴多、可複用性差、迭代週期短,今天辛辛苦苦寫的代碼,上線運行一週可能就下線了。能熟練書寫框架代碼、構建底層基礎設施的工程師不必定能寫好業務代碼。javascript

有人說,業務代碼無非就是循序漸進,優不優雅?who care?。但實際業務規則複雜得多,不是依葫蘆畫瓢就能輕鬆解決的,寫一段糟糕的代碼,可能要用雙倍的時間去發現和解決問題,麻煩了本身、也難受了和你並肩做戰的戰友。前端

有時候爲了減小重複開發的成本,反覆提煉和沉澱有複用價值的功能,就須要咱們對業務代碼進行合理抽象、甚至精雕細琢,要把業務代碼寫得優雅並不是易事。我一直認爲,程序設計和搬磚的最大區別在於設計二字,寫代碼也是一門藝術活。今天就藉此機會,站在前端工程師的視角,給你們分享關於書寫業務代碼的最佳實踐。java

0x01 漸進式重構

漸進式重構是不斷地對既有代碼進行抽象、分離和組合。作代碼重構以前須要回答兩個問題:編程

一、什麼樣的代碼須要重構?
二、什麼時候進行重構?
複製代碼

設計不是一蹴而就的,有時候寫着寫着才發現某些代碼能夠抽離出來單獨使用,須要重構的代碼須要知足幾個條件:bash

一、代碼後期可複用
二、代碼無反作用
三、代碼邏輯單一
複製代碼

過早重構可能會因需求變化太快白白浪費許多時間;過晚重構會由於代碼邏輯複雜、類似代碼積壓過多致使變動風險過高,難以維護。漸進式重構以下圖所示(紅色部分爲增長的代碼):前端工程師

enter image description here

首先咱們在同一個源文件中新增功能,發現部分代碼無反作用且可分離,所以在同一個文件中進行代碼分割,造成許多功能單一的模塊。如此往復後發現單文件的體積愈來愈大,此時就能夠將功能相關聯的模塊抽出來放到單獨的文件中統一管理,如 helpers、components、constants 等等。數據結構

0x02 高內聚低耦合

高內聚低耦合一直是軟件設計領域裏亙古不變的話題,重構的目標是提升代碼的內聚性,下降各功能間的耦合程度,下降後期維護成本,特別是寫業務代碼,這一點至關重要。框架

舉個栗子,好比新需求但願在現有的產品頁面上增長髮紅包功能,以吸引用戶開通某個功能,按照正常邏輯,我須要:函數

一、在當前頁面中引入相關依賴
二、初始化,查詢紅包相關信息
三、用戶點擊時,觸發紅包發送
複製代碼

白色部分表示上個版本的代碼,紅色部分表示完成這個需求須要變動的代碼:學習

enter image description here

這樣一來,這個發紅包功能就和之前的代碼嚴重耦合,若是這是個只須要上線一週的臨時需求,下線代碼的時候就是一個高風險的動做;若是上線運行期間還須要對產品頁面進行迭代,越日後就越搞不清楚誰是誰了。合理的設計應該是下面這個樣子的:

enter image description here

將和產品代碼無關的功能性代碼拆分出來,放到另外一個文件中內部維護好整個生命週期狀態,對外只暴露少許的接口或是方法,這樣一來對產品頁面的改造只須要:

一、引入紅包組件
二、用戶點擊時,調用紅包組件的發獎方法
複製代碼

這樣的變動是極小的、明確的、可控的。換句話說,整個紅包功能是高內聚的,與產品代碼是低耦合的。這樣實踐也帶來另外一個好處:我獲得了一個可複用的紅包組件!

0x03 合理冗餘

業務需求是多變的,寫出來的代碼也是如此,頻繁地抽象極可能致使過分設計,一個抽象極可能隨着迭代次數的增多變得十分複雜。在存在多個變量的分支業務場景,好比同時包含活動是否過時、是否已參加活動、是否完成一次任務這樣的狀況,會存在多個嵌套 if-else 結構,這時將代碼冗餘設計是個不錯的選擇。下面舉一個例子來講明什麼是合理冗餘:

e.g. 有這樣一個需求,一開始很簡單,須要設計兩個運營展位:

enter image description here

那麼抽象一個組件:

const Item = ({ title, content }) => (
  <div> <h4>{title}</h4> <p>{content}</p> </div>
);
複製代碼

如今需求要求在第一個展位的標題上增長熱文標記:

enter image description here

也很容易:

const Item = ({ title, content }, index) => (
  <div> <h4>{title}{index === 0 && <span>hot</span>}</h4> <p>{content}</p> </div>
);
複製代碼

需求又變了,要求:在第一個展位去掉內容,而且在下方加個按鈕;第二個展位的標題右邊增長一個超連接以及增長一個副標題:

enter image description here

這下有點噁心了:

const Item = ({ title, content }, index) => (
  <div>
    <h4>
      {title}
      {index === 0 && <span>hot</span>}
      {index === 1 && <a href="xxx">去看看</a>}
    </h4>
    {index === 1 && <h5>副標題</h5>}
    <p>
      {index !== 0 && content}
      {index === 0 && <button>領福利<button>}
    </p>
  </div>
);
複製代碼

能夠看到,以前抽象的好好的,如今需求一變,代碼就面目全非了,中間混雜着兩個狀態(第一個、第二個)的判斷邏輯。實際狀況極可能比這個更復雜,在多狀態交織邏輯難以經過一套代碼表達清楚時,進行合理冗餘就是個不錯的選擇,將上面的例子用兩個 if 重寫以下:

// 第一個展位
if (index === 0) {
  return (
    <div>
      <h4>標題一<span>hot</span></h4>
      <p><button>領福利<button></p>
    </div>
  );
}
// 第二個展位
if (index === 1) {
  return (
    <div>
      <h4>標題二<a href="xxx">去看看</a></h4>
      <h5>副標題</h5>
      <p>內容</p>
    </div>
  );
}
複製代碼

合理冗餘其實也是一種重構,根據業務邏輯和代碼規模,作類似抽象仍是代碼冗餘,這其實也是漸進式重構的一種體現。不管採用何種方式,只要能把業務邏輯表達清楚,讓代碼始終保持良好的可讀性和可維護性,就OK。

下面介紹一個過分抽象的例子。

0x04 拒絕過分抽象

在 JavaScript 代碼中進行深度抽象有時並不是好事,有 OOP(面向對象編程)背景的同窗很容易先入爲主設計:全部數據結構都想封裝成一個類 (Class) 。實際上 Class 在 JavaScript 中是個很差的設計,它並不是真正的類。幾年前,我曾看到一位 Java 轉前端的同窗寫出了相似這樣的代碼:

class DataItem {
  constructor(id, name, value) {
    this.id = id;
    this.name = name;
    this.value = value;
  }
}

class DataCollection {
  constructor() {
    this.items = new Array();
  }
  insert(item) {
    this.items.push(item);
  }
}

const item1 = new DataItem(1, 'name1', 100);
const item2 = new DataItem(2, 'name2', 200);
const list = new DataCollection();
list.insert(item1);
list.insert(item2);
...
複製代碼

一股濃濃的 Java 味道撲面而來。上面的代碼並無發揮出 JavaScript 的語言優點,也增長了很多理解成本,若是用面向對象編程的思路去寫前端代碼,特別是業務代碼,可真是一場噩夢。正確的寫法以下:

const list = [{
  id: 1,
  name: 'name1',
  value: 100
}, {
  id: 2,
  name: 'name2',
  value: 200
}];
複製代碼

因爲 JS 屬於弱類型語言,弱類型語言就要發揮弱類型的優點,無需過多類型定義和 Class 抽象,用最原始的 object 和 function 足以勝任從簡單到複雜的業務場景。這裏特別想說起前端所熟知的 Redux 狀態管理器,Redux 中,state 就是普通的 object,reducer 就是普通的 function,action 也是普通的 object,不加任何類型約束。由於簡單,因此強大。

0x05 眼觀六路

用弱類型語言編程意味着無需編譯,無需編譯的語言天生存在一個問題是在運行前缺乏必要的類型檢查,將問題暴露在運行時每每會致使很是嚴重的故障。這就要求開發者能在寫代碼的階段嚴格保證代碼質量,特別是寫業務代碼。

集成開發環境(IDE)對 JavaScript 代碼的智能提示能力有限,不少時候不能經過 IDE 查找某個變量或者函數的全部引用,這時就要善用 Ctrl + F 進行全局查找來保證本身的單點變動不會影響到其餘地方。若是使用 TypeScript,在類型檢查、引用查找上的幫助會更好。

0x06 總結

今天給你們分享了關於書寫業務代碼的一些實踐經驗:對代碼進行漸進式重構是提高代碼健壯性的有力武器;設計高內聚低耦合的代碼可讓你在作需求的過程當中沉澱出一套通用解決方案;合理冗餘能夠簡化複雜的場景,讓開發變得高效、測試變得容易;拒絕過分抽象,擁抱簡單,靈活變化。保持 眼觀六路 的好習慣能讓代碼質量提高一個臺階。

最後,但願你們能在實際開發過程當中去體會和學習,不斷思考和總結,將業務代碼寫優雅,是個很大的挑戰。


關於咱們:

咱們是螞蟻保險體驗技術團隊,來自螞蟻金服保險事業羣。咱們是一個年輕的團隊(沒有歷史技術棧包袱),目前平均年齡92年(去除一個最高分8x年-團隊leader,去除一個最低分97年-實習小老弟)。咱們支持了阿里集團幾乎全部的保險業務。18年咱們產出的相互寶轟動保險界,19年咱們更有多個重量級項目籌備動員中。現伴隨着事業羣的高速發展,團隊也在迅速擴張,歡迎各位前端高手加入咱們~

咱們但願你是:技術上基礎紮實、某領域深刻(Node/互動營銷/數據可視化等);學習上善於沉澱、持續學習;性格上樂觀開朗、活潑外向。

若有興趣加入咱們,歡迎發送簡歷至郵箱:yiyuan.lpy@antfin.com


本文做者:螞蟻保險-體驗技術組-禕遠

掘金地址:micooz

相關文章
相關標籤/搜索