前端設(shè)計模式之裝飾模式

前言

傳送地址:

前端設(shè)計模式之工廠模式

前端設(shè)計模式之代理模式

前端設(shè)計模式之策略模式

設(shè)計模式第四篇,來一個與之前代理模式容易混淆的裝飾模式(本來打算寫觀察者模式,此處再次實名 diss 某人的埋點博客寫的速度)

裝飾器模式

什么是裝飾模式?

裝飾(Decorator)模式的定義:指在不改變現(xiàn)有對象結(jié)構(gòu)的情況下,動態(tài)地給該對象增加一些職責(zé)(即增加其額外功能)的模式,它屬于對象結(jié)構(gòu)型模式。此處引用了 gof 設(shè)計模式的描述。

裝飾(Decorator)模式的主要優(yōu)點有:

  • 采用裝飾模式擴(kuò)展對象的功能比采用繼承方式更加靈活。
  • 可以設(shè)計出多個不同的具體裝飾類,創(chuàng)造出多個不同行為的組合。

其主要缺點是:裝飾模式增加了許多子類,如果過度使用會使程序變得很復(fù)雜。

裝飾模式跟代理模式的區(qū)分

首先兩者都是對方法的補(bǔ)充,故而在功能上是有重疊的部分,所以在看代理模式的時候會存在一些疑惑,這倆功能不是差不多的嗎?這里簡單區(qū)分一下二者的特點

代理模式重在于對方法的控制,添加行為對于用戶是被動的;

裝飾模式重在于裝飾方法,增加方法的功能,添加裝飾對于用戶是主動的。

也就是說,代理模式在使用的時候,被代理的對象不會被修改,功能是單一且已知,對代理的過程無感,被代理的對象既不知道代理過程做了什么,也不會因為代理而改變他自己本身的屬性

而裝飾模式是在裝飾源對象的過程中就拓展了功能與屬性,源對象本身的屬性就已經(jīng)被執(zhí)行的時候就已經(jīng)被動態(tài)的修改了。

兩者對于開發(fā)者最大的區(qū)別來說,裝飾模式是開發(fā)者主動去修改源對象,代理模式是去被動的拓展源對象的功能。

裝飾模式跟代理模式通俗理解

好的,回歸到我們的初心-開寵物店的栗子中

我們在代理模式中提到過,我們可以選擇代理、加盟的模式去將我們的生意擴(kuò)大開來。但是隨之而來有一定的問題,作為代理商,他們的選擇可以多樣化,我們的基礎(chǔ)服務(wù)之外,他們會將自身的經(jīng)營想法跟管理理念融入進(jìn)去,除了貨物源頭之外,我們實際上沒辦法對代理商進(jìn)行很高的約束。這樣的話,代理商可能會在賣寵物的時候,搭配一些比較辣雞的服務(wù)(過期寵物糧食、劣質(zhì)的寵物牢籠等)來影響我們自己自身的品牌效應(yīng)。

既然有這個情況出現(xiàn),那我們可以加入直營店的概念或者在我們提供基礎(chǔ)服務(wù)的時候,主動的將拓展服務(wù)提供出來,從而維護(hù)我們寵物店的品牌與影響力。

項目實戰(zhàn)

業(yè)務(wù)發(fā)送 ajax 請求

const method = (type, url) => {
    switch (type) {
        case 'GET': {
            return (target, name, descriptor) => {
                return {
                    ...descriptor,
                    value(query) {
                        fetch.get({
                            url,
                            query
                        }).then(data => {
                            descriptor.value.apply({ result: data }, [...arguments])
                        }).catch(err => {
                            console.log('err====>', err)
                        })
                    }
                }
            }
        }
        default: {
            return (target, name, descriptor) => {
            }
        }
    }
}

class Business {
    @method('GET', 'https://api.github.com/users/octocat')
    getOct(params) {
        console.log(params)
        console.log('result==>', this.result)
    }
}

const business = new Business()

business.getOct({
    test: 1
})

上述是將業(yè)務(wù)方法使用裝飾模式優(yōu)雅的包了一層,實際項目中如果有簡單的業(yè)務(wù)請求,這么使用可以優(yōu)雅一點,跟上一篇 fetch 實戰(zhàn)項目篇最后介紹業(yè)務(wù)請求封裝不太一樣,這種是將請求直接寫在了業(yè)務(wù)方法當(dāng)中,優(yōu)點是簡潔明了,可以迅速了解到此業(yè)務(wù)的請求 url 跟方式,缺陷是多重嵌套請求不好處理

添加埋點

function consuming(target, name, descriptor) {
    return {
        ...descriptor,
        value(query) {
            try {
                console.time('consuming')
                console.log('發(fā)送埋點', query)
                descriptor.value.apply(this, [...arguments])
            } finally {
                console.timeEnd('consuming')
            }
        }
    }
}

class Business {

    @consuming
    @method('GET', 'https://api.github.com/users/octocat')
    getOct(params) {
        console.log(params)
        console.log('result==>', this.result)
    }
}

裝飾模式可以自由的將多種裝飾器組合使用,類似于洋蔥模式,所以我們可以將埋點功能使用裝飾器添加在方法中。

函數(shù)-防抖與節(jié)流

眾所周知,防抖函數(shù)在日常開發(fā)中還是比較常見的一種需求,so 我們也可以將防抖函數(shù)已裝飾器模式靈活的添加進(jìn)去

function debounce(wait, immediate) { // 此處防抖函數(shù),可以延遲跟立即執(zhí)行兩種
    let timer;
    return (target, name, descriptor) => {
        return {
            ...descriptor,
            value(query) {
                if (timer) clearTimeout(timer);
                if (immediate) {
                    let callNow = !timer;
                    timer = setTimeout(() => {
                        timer = null;
                    }, wait);
                    if (callNow) descriptor.value.apply(this, [...arguments]);
                } else {
                    timer = setTimeout(() => {
                        descriptor.value.apply(this, [...arguments])
                    }, wait)
                }
            }
        }
    }
}

class Business {
    @debounce(3000)
    @consuming
    @method('GET', 'https://api.github.com/users/octocat')
    getOct(params) {
        console.log(params)
        console.log('result==>', this.result)
    }
}

既然防抖可以,那么節(jié)流亦可

function throttle(wait) {
    let timeout;
    return (target, name, descriptor) => {
        return {
            ...descriptor,
            value(query) {
                if (!timeout) {
                    timeout = setTimeout(() => {
                        timeout = null;
                        descriptor.value.apply(this, [...arguments])
                    }, wait)
                }
            }
        }
    }
}

以上是裝飾模式在項目中的實例使用,部分可以直接使用,部分是作為技術(shù)探討。

尾聲

完整的 demo 地址:項目實戰(zhàn) demo,喜歡的朋友可以 star 一下,后續(xù)會根據(jù)設(shè)計模式博文的推出,逐步的將此項目繼續(xù)拓展出來。

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

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

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