一種通用的設(shè)計(jì)模式: Pattern Mapping

Why

在最近的設(shè)計(jì)過(guò)程中,我發(fā)現(xiàn)我不止一次的使用一種類(lèi)似的設(shè)計(jì)方案,這種設(shè)計(jì)方案會(huì)從紛繁復(fù)雜的表象中發(fā)現(xiàn)(或者說(shuō)理清)業(yè)務(wù)的本質(zhì),使代碼變得簡(jiǎn)單,系統(tǒng)更容易維護(hù)。第一次運(yùn)用這種技巧已經(jīng)是幾年前的事情了,因?yàn)樽罱謨纱斡玫竭@個(gè)技巧,因此決定把這種技巧寫(xiě)下來(lái),并給它取了一個(gè)名字: Pattern Mapping, 中文名叫 模式映射 。

先來(lái)看一個(gè)例子,我們有一個(gè)系統(tǒng),它的其中一個(gè)業(yè)務(wù)是把數(shù)據(jù)從一種 源數(shù)據(jù)庫(kù)(Source) 同步到另外一種 目標(biāo)數(shù)據(jù)庫(kù)(Target) , 而不同數(shù)據(jù)庫(kù)之間進(jìn)行同步的時(shí)候,為了達(dá)到最高的性能,會(huì)采用不同的同步方式,比如(這里只是為了說(shuō)明問(wèn)題,不代表真實(shí)實(shí)現(xiàn)):

  • MySQL -> Spark: 采用MR
  • MySQL -> Oracle: 采用DataX
  • MySQL -> HBase: 采用HBaseBulkLoad
問(wèn)題示意

一種比較原始的做法是針對(duì)每種不同的Source -> Target組合,我們都各自編碼,寫(xiě)著寫(xiě)著,你會(huì)發(fā)現(xiàn)有些組合的業(yè)務(wù)邏輯是類(lèi)似的,比如MySQL -> Oracle, MySQL -> PostgreSQL都是用DataX同步,于是你的代碼里面會(huì)出現(xiàn)一些if-else判斷邏輯:

    if ((source == MySQL && target == Oracle)
       || (source == MySQL && target == PostgreSql)) {
        // balabalabala
    }

    ...

    if ((source == aaa && target == bbb)
       || (source == ccc && target == ddd)) {
        // balabalabala
    }

    ...
    if ((source == eee && target == fff)
       || (source == ggg && target == hhh)) {
        // balabalabala
    }
    ...

在系統(tǒng)開(kāi)發(fā)的初期,這種if-else邏輯不多的時(shí)候,還沒(méi)有什么問(wèn)題。但是當(dāng)我們支持的數(shù)據(jù)源越來(lái)越多時(shí)候,你會(huì)發(fā)現(xiàn)這種if-else如此之多,以至于你已經(jīng)沒(méi)辦法理清所有的業(yè)務(wù)邏輯;你要新增對(duì)于一種數(shù)據(jù)庫(kù)的支持的時(shí)候,你不知道到底哪些if-else需要進(jìn)行修改。因?yàn)檫@些SourceTarget之間產(chǎn)生了類(lèi)似笛卡爾積的關(guān)系,凡人是理不清這之前的關(guān)系的:

理不清的業(yè)務(wù)規(guī)則

代碼開(kāi)始腐爛,人員開(kāi)始流失,可能要考慮推倒重來(lái)了。

Pattern Mapping

等等,讓Pattern Mapping來(lái)試試拯救你。

Pattern Mapping,或者說(shuō)模式映射,是指針對(duì)一組紛繁復(fù)雜的業(yè)務(wù),我們不是直接針對(duì)裸的原始輸入進(jìn)行業(yè)務(wù)規(guī)則的定義,而是從中抽象出其本質(zhì)的幾種Pattern, 然后把原始輸入跟Pattern進(jìn)行Mapping,從而構(gòu)造出整個(gè)系統(tǒng)。

這些Pattern其實(shí)才是業(yè)務(wù)的本質(zhì),因此這樣理出的Pattern的個(gè)數(shù)一般會(huì)比原始數(shù)據(jù)源的個(gè)數(shù)小一個(gè)數(shù)量級(jí),從而降低了整個(gè)問(wèn)題的復(fù)雜度。比如說(shuō)上面的例子,我們其實(shí)可以抽象出幾種Pattern:

Pattern Mapping
可以用DataX同步的 MySQL -> Oracle, MySQL -> Postgresql
可以用BulkLoad同步的 任意數(shù)據(jù)庫(kù) -> HBase
可以用MR同步的 Spark -> MySQL

這樣代碼就變成了:

    if (isDataXCompatiable(source, target)) {
        // 使用DataX同步
    }

    ...

    if (isBulkLoadCompatible(source, target)) {
        // 使用BulkLoad進(jìn)行同步
    }

    ...
    if (isMRCompatible(source, target)) {
        // 使用MR進(jìn)行同步
    }

你會(huì)發(fā)現(xiàn)這個(gè)代碼里面沒(méi)有任何地方對(duì)具體的 source, target 的類(lèi)型進(jìn)行判斷,我們編程針對(duì)的是業(yè)務(wù)的本質(zhì),而不是裸的業(yè)務(wù)輸入,從而減少了這種if-else的個(gè)數(shù),在原來(lái)的笛卡爾積問(wèn)題中間加入了中間的Pattern, 降低了問(wèn)題的復(fù)雜度:

Pattern Mapping

它把原來(lái)的 M x N 的問(wèn)題 變成了 M + X + N 的問(wèn)題。

結(jié)論

Pattern Mapping適用的場(chǎng)景是: 業(yè)務(wù)邏輯的輸入因子有很多可能的值,這些因子之間可能會(huì)相互作用,但是不同因子之間是有一些共通的Pattern。使用Pattern Mapping使得我們可以抓住業(yè)務(wù)的本質(zhì),使得我們面向業(yè)務(wù)的本質(zhì)編程(而不是裸的業(yè)務(wù)輸入), 降低了業(yè)務(wù)的復(fù)雜性,提高了代碼的可維護(hù)性。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容