<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          精益求精!記一次業(yè)務(wù)代碼的優(yōu)化探索

          共 7552字,需瀏覽 16分鐘

           ·

          2021-09-10 16:01

          關(guān)鍵詞:需求實(shí)現(xiàn)、設(shè)計(jì)模式、策略模式、程序員成長(zhǎng)

          本篇文章由淘系新人喜橙同學(xué)撰寫。

          承啟:

          本篇從業(yè)務(wù)場(chǎng)景出發(fā),介紹了面對(duì)一個(gè)復(fù)雜需求,拆解重難點(diǎn)、編碼實(shí)現(xiàn)需求、優(yōu)化代碼、思考個(gè)人成長(zhǎng)的過程。

          • 會(huì)介紹一個(gè)運(yùn)用策略模式的實(shí)戰(zhàn)。
          • 需求和編碼本身小于打怪升級(jí)成長(zhǎng)路徑。
          • 文中代碼為偽代碼。

          場(chǎng)景說明:

          需求描述:手淘內(nèi)“充值中心”要投放在餓了么、淘寶極速版、UC瀏覽器等集團(tuán)二方APP。拿到需求之后,來梳理下“充值中心”在他端投放涉及到的核心功能點(diǎn)

          • 通訊錄讀取 不同客戶端、操作系統(tǒng),JSbridge API實(shí)現(xiàn)略有不同。
          • 支付 不同端支付JSbridge調(diào)用方式不同。
          • 賬號(hào)體系:集團(tuán)內(nèi)不同端賬號(hào)體系可能不同,需要打通。
          • 容器兼容 手淘內(nèi)采用PHA容器,淘寶極簡(jiǎn)版本投放H5,餓了么以手淘小程序的方式投放。環(huán)境變量、通信方式等需要兼容。
          • 各端個(gè)性化訴求 極速版投放極簡(jiǎn)鏈路,只保留核心模塊等。

          解決方案

          需求明確了:充值相關(guān)核心模塊,需要兼容每個(gè)APP,本質(zhì)是提供一個(gè)多端投放的解決方案。那么這個(gè)場(chǎng)景如何編碼實(shí)現(xiàn)呢?

          1、方案一

          首先第一個(gè)想法??,在每個(gè)功能點(diǎn)模塊用if-else判斷客戶端環(huán)境,編寫此端邏輯。下面以獲取通訊錄列表功能為例,代碼如下:

          // 業(yè)務(wù)代碼文件 index.js
          /**
           * 獲取通訊錄列表
           * @param clientName 端名稱
           */

          const getContactsList = (clientName) => {
            if (clientName === 'eleme') {
              getContactsListEleme()
            } else if (clientName === 'taobao') {
              getContactsListTaobao()
            } else if (clientName === 'tianmao') {
              getContactsListTianmao()
            } else if (clientName === 'zhifubao') {
              getContactsListZhifubao()
            } else {
              // 其他端
            }
          }

          寫完之后,review一下代碼,思考一下這樣編碼的利弊。

          :邏輯清晰,可快速實(shí)現(xiàn)。

          :代碼不美觀、可讀性略差,每兼容一個(gè)端都要在業(yè)務(wù)邏輯處改動(dòng),改一端測(cè)多端。

          這時(shí),有的同學(xué)就說了:“把if-else改成switch-case的寫法,把獲取通訊錄模塊抽象成獨(dú)立的sdk封裝,用戶在業(yè)務(wù)層統(tǒng)一調(diào)用”,天才!動(dòng)手實(shí)現(xiàn)一下。

          2、方案二

          核心功能模塊,抽象成獨(dú)立的sdk,模塊內(nèi)部對(duì)不同的端進(jìn)行兼容,業(yè)務(wù)邏輯里統(tǒng)一方式調(diào)用。

          /**
           * 獲取通訊錄列表 sdk caontact.js
           * @param clientName 端名稱
           * @param successCallback 成功回調(diào)
           * @param failCallback 失敗回調(diào)
           */

          export default function (clientName, successCallback, failCallback{
            switch (clientName) {
              case 'eleme':
                getContactsListEleme()
                break
              case 'taobao':
                getContactsListTaobao()
                break
              case 'zhifubao':
                getContactsListTianmao()
                break
              case 'tianmao'
                getContactsListZhifubao()
                break
              default:
                // 省略
                break
            }
          }

          // 業(yè)務(wù)調(diào)用 index.js
          <Contacts onIconClick={handleContactsClick} />

          import getContactsList from 'Contacts'
          import { clientName } from 'env'
          const handleContactsClick = () => {
            getContactsList(
              clientName,
              ({ arr }) => {
                this.setState({
                  contactsList: arr
                })
              },
              () => {
                alert('獲取通訊錄失敗')
              }
            )
          }

          慣例,review一下代碼:

          :模塊分工明確,業(yè)務(wù)層統(tǒng)一調(diào)用,代碼可讀性較高。

          :多端沒有解藕,每次迭代,需要各個(gè)端回歸。

          上面的實(shí)現(xiàn),看起來代碼可讀性提高了不少,是一個(gè)不錯(cuò)的設(shè)計(jì),可是這樣是最優(yōu)的設(shè)計(jì)嗎?

          3、方案三

          熟悉設(shè)計(jì)模式的同學(xué),這時(shí)候可能要說了,用策略模式啊,對(duì)了,這個(gè)場(chǎng)景可以用策略模式。這里簡(jiǎn)單解釋一下策略模式:策略模式,英文全稱是 Strategy Design Pattern。在 GoF 的《設(shè)計(jì)模式》一書中,它是這樣定義的:

          Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.

          翻譯成中文就是:定義一族算法類,將每個(gè)算法分別封裝起來,讓它們可以互相替換。策略模式可以使算法的變化獨(dú)立于使用它們的客戶端(這里的客戶端代指使用算法的代碼)。

          難免有些晦澀,什么意思呢?我個(gè)人的理解為:策略模式用來解耦策略的定義、創(chuàng)建、使用。它典型的應(yīng)用場(chǎng)景就是:避免冗長(zhǎng)的if-else或switch分支判斷編碼。

          下面看代碼實(shí)現(xiàn):

          /**
           * 策略定義
           */

          const strategies = {
            eleme() => {
              getContactsListEleme()
            },
            taobao() => {
              getContactsListTaobao()
            },
            tianmao() => {
              // 省略
            }
          }
          /**
           * 策略創(chuàng)建
           */

          const getContactsStrategy = (clientName) => {
            if (!clientName) {
              throw new Error('clientName is empty.')
            }
            return strategies[clientName]
          }
          /**
           * 策略使用
           */

          import { clientName } from 'env'
          getContactsStrategy(clientName)()

          策略模式的運(yùn)用,把策略的定義、創(chuàng)建、使用解耦,符合設(shè)計(jì)原則中的迪米特法則(LOD),實(shí)現(xiàn)“高內(nèi)聚、松耦合”。當(dāng)需要新增一個(gè)適配端時(shí),我們只需要修改策略定義Map,其他代碼都不需要修改,這樣就將代碼改動(dòng)最小化、集中化了。

          能做到這里,相信你已經(jīng)超越了一部分同學(xué)了,但是我們還要思考、精益求精,如何更優(yōu)呢?這個(gè)時(shí)候單從編碼層面思考已經(jīng)受阻塞了,可否從工程構(gòu)建角度、性能優(yōu)化角度、項(xiàng)目迭代流程角度、后期代碼維護(hù)角度思考一下,相信你會(huì)有更好的想法。

          下面拋磚,聊聊我自己的思考:

          4、方案四

          從工程構(gòu)建和性能優(yōu)化角度出發(fā):如果每個(gè)端獨(dú)立一個(gè)文件,構(gòu)建的時(shí)候shake掉其他端chunk,這樣bundle可以變更小,網(wǎng)絡(luò)請(qǐng)求也變更快。

          等等... Tree-Shaking是基于ES靜態(tài)分析,我們的策略判斷,基于運(yùn)行時(shí),好像可能沒什么用啊。

          方案三使用策略模式來編碼,本質(zhì)是策略定義、創(chuàng)建和使用解藕,那可否使用剛才的想法,把每端各個(gè)功能模塊兼容方法聚合成獨(dú)立module,從更高維度,將多端業(yè)務(wù)策略定義、創(chuàng)建和使用解藕?

          思考一下這樣做的收益是什么?

          因?yàn)槊總€(gè)端的適配,聚合在一個(gè)module,將多端業(yè)務(wù)策略解藕,某個(gè)端策略變更,只需要修改此端module,代碼改動(dòng)較小,且后續(xù)測(cè)試鏈路,不需要重復(fù)回歸其他端。符合“高內(nèi)聚、松耦合”。

          代碼實(shí)現(xiàn):

          /**
           * 餓了么端策略定義module
           */

          export const elmcStrategies = {
            contacts() => {
              getContactsListEleme()
            },
            pay() => {
              payEleme()
            },
            // 其他功能略
          }
          /**
           * 手淘端策略定義module
           */

          export const tbStrategies = {
            contacts() => {
              getContactsListTaobao()
            },
            pay() => {
              payTaobao()
            },
            // 其他功能略
          };
          // ...... (其他端略)
          /**
           * 策略創(chuàng)建 index.js
           */

          import tbStrategies from './tbStrategies'
          import elmcStrategies from './elmcStrategies'
          export const getClientStrategy = (clientName) => {
            const strategies = {
              elmc: elmcStrategies,
              tb: tbStrategies
              // ...
            }
            if (!clientName) {
              throw new Error('clientName is empty.')
            }
            return strategies[clientName]
          };
          /**
           * 策略使用 pay
           */

          import { clientName } from 'env'
          getClientStrategy(clientName).pay()

          代碼目錄如下圖所示:



          index.js是多端策略的入口,其他文件為各端策略實(shí)現(xiàn)。

          從方案四的推導(dǎo)來看,有時(shí)候,判斷不一定是對(duì)的,但是從多個(gè)維度去思考,會(huì)打開思路,這時(shí),更優(yōu)方案往往就找上門來了~

          5、方案五

          既要解決眼前痛點(diǎn),也要長(zhǎng)遠(yuǎn)謀劃,基于以上四種方案,再深入思考一步,如果業(yè)務(wù)有投放在第三方(非集團(tuán)APP)的需求,比如投放在商家APP,且商家APP獲取通訊錄、支付邏輯等復(fù)雜多變,這個(gè)時(shí)候如何設(shè)計(jì)編碼呢?例如:拉起別端的喚端策略,受多方因素影響,涉及到產(chǎn)品壁壘,策略攻防,怎樣控制代碼改動(dòng)次數(shù),及時(shí)提高喚端率呢?在這里簡(jiǎn)單拋磚,可以借助近幾年很火的serverless,搭建喚端策略的faas函數(shù),動(dòng)態(tài)獲取最優(yōu)喚端策略,是不是一個(gè)好的方案呢?

          沉淀&思考

          以上針對(duì)多端兼容的問題,我們學(xué)習(xí)并運(yùn)用了設(shè)計(jì)模式——策略模式。那么我們?cè)賮砜纯床呗阅J降脑O(shè)計(jì)思想是什么:一提到策略模式,有人就覺得,它的作用是避免 if-else 分支判斷邏輯。實(shí)際上,這種認(rèn)識(shí)是很片面的。策略模式主要的作用還是解耦策略的定義、創(chuàng)建和使用,控制代碼的復(fù)雜度,讓每個(gè)部分都不至于過于復(fù)雜、代碼量過多。除此之外,對(duì)于復(fù)雜代碼來說,策略模式還能讓其滿足開閉原則,添加新策略的時(shí)候,最小化、集中化代碼改動(dòng),減少引入 bug 的風(fēng)險(xiǎn)。實(shí)際上,設(shè)計(jì)原則和思想比設(shè)計(jì)模式更加普適和重要。掌握了代碼的設(shè)計(jì)原則和思想,我們能更清楚的了解,為什么要用某種設(shè)計(jì)模式,就能更恰到好處地應(yīng)用設(shè)計(jì)模式。還有一點(diǎn)需要注意,在代碼設(shè)計(jì)時(shí),應(yīng)該了解他的業(yè)務(wù)價(jià)值和復(fù)雜度,避免過度設(shè)計(jì),如果一個(gè)if-else可以解決的問題,何必大費(fèi)周折,闊談設(shè)計(jì)模式呢?

          總結(jié)

          理一下全文的核心路徑,也是我此篇文章想要主要傳達(dá)的打怪升級(jí)成長(zhǎng)路徑。

          接到一個(gè)復(fù)雜的需求--> 理清需求 --> 拆解技術(shù)難點(diǎn) --> 編碼實(shí)現(xiàn) --> 代碼優(yōu)化 --> 設(shè)計(jì)模式和設(shè)計(jì)原則學(xué)習(xí) --> 舉一反三 --> 記錄沉淀。

          當(dāng)下,前端工程師在工作中,難免會(huì)陷入業(yè)務(wù)漩渦中,被業(yè)務(wù)推著走。面對(duì)這種風(fēng)險(xiǎn),我們要思考如何在保障完成業(yè)務(wù)迭代的基礎(chǔ)上,運(yùn)用適合的技術(shù)架構(gòu),抽象出通用解決方案,沉淀落地。這樣,既能幫助業(yè)務(wù)更快更穩(wěn)定增長(zhǎng),又能在這個(gè)過程中收獲個(gè)人成長(zhǎng)。

          瀏覽 30
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  台湾精品一区二区三区四区 | 大雞巴弄得我好舒服黃片动漫版 | 逼色网站亚洲 | 国产和美国黄色毛片 | 在线看片国产 |