讀代碼之golang裝飾器模式

v站看到一篇文章,說到了golang實(shí)現(xiàn)python裝飾器的方法,有兩個(gè)實(shí)現(xiàn)方式
其中一種是用類方式實(shí)現(xiàn)的,當(dāng)時(shí)泛覽了下代碼,覺得 emmm...實(shí)現(xiàn)方式挺花哨的,貌似看懂了就沒怎么在意,當(dāng)時(shí)特意把代碼復(fù)制到goland中作為參考了
某天上班摸魚,仔細(xì)看了下這段代碼,梳理細(xì)節(jié)后忽然發(fā)現(xiàn)沒get到其中邏輯
郁悶很久未參透
有空就特意調(diào)試打印下這段代碼
好幾天才整明白,這大概就是菜比の理解能力了吧

代碼如下:

// with interface. which we need create a `point` first. and use it later

package main

import "fmt"

type FibI interface {
    Fib(n int) int
    Wrap(fib FibI) FibI
}

type Fib struct {
    Wrapper FibI
}

func (this *Fib) Fib(n int) int {
    //wrapper := this.Wrapper
    if this.Wrapper == nil {
        this.Wrapper = this
    }
    fmt.Printf("我執(zhí)行了...%T...%v\n", this.Wrapper, n)
    if n == 0 {
        return 0
    }
    if n == 1 {
        return 1
    }
    // call son
    fmt.Printf("leixing9...%T...\n", this.Wrapper)
    return this.Wrapper.Fib(n-1) + this.Wrapper.Fib(n-2)
}

func (this *Fib) Wrap(fib FibI) FibI {
    fmt.Printf("leixing1...%T...\n", this.Wrapper)
    this.Wrapper = fib
    fmt.Printf("leixing2...%T...\n", this.Wrapper)
    fmt.Println("fib 調(diào)用", this)
    return this
}

type CacheFib struct {
    Wrapper FibI
    cache   map[int]int
}

func (this *CacheFib) Wrap(fib FibI) FibI {
    this.Wrapper = fib
    fmt.Printf("leixing6...%T...\n", this.Wrapper)
    return this
}

func (this *CacheFib) Fib(n int) int {
    if this.cache == nil {
        this.cache = make(map[int]int)
    }
    if ans, ok := this.cache[n]; ok {
        return ans
    }
    ans := this.Wrapper.Fib(n)
    this.cache[n] = ans
    return ans
}

type CounterFib struct {
    Wrapper FibI
    Counter int
}

func (this *CounterFib) Wrap(fib FibI) FibI {
    this.Wrapper = fib
    fmt.Printf("leixing...%T...\n", this.Wrapper)
    return this
}

func (this *CounterFib) Fib(n int) int {
    this.Counter++
    return this.Wrapper.Fib(n)
}

func main() {
    fib := new(Fib)
    //fmt.Println("result fib", fib.Fib(10))

    cacheFib := new(CacheFib)
    //f := cacheFib.Wrap(fib.Wrap(cacheFib))
    //fmt.Println(f.Fib(10), "heihei")
    counterFib := new(CounterFib)
    counterCacheFib := cacheFib.Wrap(counterFib.Wrap(fib.Wrap(cacheFib)))
    fmt.Println("result cache:counter:fib", counterCacheFib.Fib(10))
    fmt.Printf("count: %d, cache: %v", counterFib.Counter, cacheFib.cache)
}

本文的目的沒有那么高大上,就是簡(jiǎn)單的理解一下,這種裝飾器實(shí)現(xiàn)及其后的核心原理
講真,這個(gè)代碼實(shí)現(xiàn)的挺花哨的>_<
所以,我把自己的那些調(diào)試打印信息也一并給出了

首先第一步:
背景:用裝飾器的原理實(shí)現(xiàn)斐波那契數(shù)列的計(jì)算
如果有g(shù)olang語(yǔ)法基礎(chǔ),相信這些main函數(shù)之前的代碼應(yīng)該理解起來不難

其中Fib結(jié)構(gòu)是一個(gè)實(shí)現(xiàn)了FibI接口的對(duì)象
乍看之下好像僅此而已
CacheFib結(jié)構(gòu)也同樣實(shí)現(xiàn)了此接口,不過提供了一個(gè)map數(shù)據(jù)結(jié)構(gòu),會(huì)把之前計(jì)算過的數(shù)值緩存起來,后面調(diào)用直接取緩存
CounterFib結(jié)構(gòu)就簡(jiǎn)單啦,它計(jì)算了CounterFib實(shí)例調(diào)用Fib方法的次數(shù)
三個(gè)Fib類的Wrap方法實(shí)現(xiàn)方式都一樣:就是把自身的Wrapper屬性賦值為參數(shù)中的(實(shí)現(xiàn)了)FibI值

一直到main函數(shù)之前,這些代碼還都不難理解
最后注意下主函數(shù)內(nèi)的調(diào)用方法

cacheFib.Wrap(counterFib.Wrap(fib.Wrap(cacheFib)))

瞬間懵逼有木有
乍看之下,為什么cacheFib會(huì)出現(xiàn)兩次,看起來好像是自己引用了自己(遞歸也是自己調(diào)用自己,好好參悟一下)
嗯,那些能明白這行代碼的童鞋可以先行告退了,沒懂得同學(xué)咱門繼續(xù)

不明白的話就運(yùn)行一遍代碼:
斐波那契數(shù)列計(jì)算完美,有計(jì)算次數(shù),也優(yōu)化了計(jì)算邏輯(緩存了)
但這并不妨礙咱們的懵逼狀態(tài)ing~
整個(gè)方法的調(diào)用鏈?zhǔn)窃鯓拥哪?/p>

我在各個(gè)位置加了打印信息,其核心就是Fib方法
通過打印可以發(fā)現(xiàn),每次計(jì)算n數(shù)值的時(shí)候都會(huì)調(diào)用Fib 對(duì)象的Fib 方法(有點(diǎn)繞)
打印Fib 對(duì)象的Wrapper類型, 顯示為main.CacheFib類型

所以,我們重新回顧下Fib對(duì)象,其實(shí)它沒那么簡(jiǎn)單,首先對(duì)象的Wrapper 屬性本身類型就是實(shí)現(xiàn)了FibI 接口的類型,其次對(duì)象本身也實(shí)現(xiàn)FibI 接口
Fib 對(duì)象的Fib 方法中調(diào)用了 Fib 對(duì)象的Wrapper屬性的Fib方法(還是很繞,注意區(qū)分下Fib對(duì)象和Fib方法,這是兩個(gè)概念)

那么回頭來看main函數(shù)中的實(shí)現(xiàn),cacheFib 是CacheFib類的實(shí)例,cacheFib使用Wrap方法用counterFib對(duì)象來裝飾自身,但是counterFib對(duì)象的Wrapper被fib對(duì)象裝飾了,同時(shí)fib對(duì)象又被cacheFib自身裝飾了,那么當(dāng)我們執(zhí)行counterCacheFib.Fib(10)是時(shí)候,經(jīng)過層層裝飾,其實(shí)調(diào)用的方法是fib對(duì)象的Fib方法,fib對(duì)象的Fib方法在每次執(zhí)行的時(shí)候又會(huì)調(diào)用自身Wrapper的Fib方法(不過這是一個(gè)類似遞歸的操作,只有當(dāng)參數(shù)n大于1的時(shí)候才會(huì)調(diào)用自身的Wrapper的Fib方法),不過Fib.Wrapper又被cacheFib裝飾了,所以繞了一圈又回來了,只要入?yún)大于1,就會(huì)一直遞歸往復(fù)(這是一個(gè)有退出條件的閉環(huán)), 所以小伙伴們都繞明白了嗎

如果用python實(shí)現(xiàn)起來其實(shí)就相對(duì)簡(jiǎn)單多了,這里就不再給出代碼了。換個(gè)思路,為了加強(qiáng)理解,如果用python實(shí)現(xiàn)一個(gè)上面golang風(fēng)格的代碼呢?感興趣可以實(shí)現(xiàn)一下,會(huì)更好的幫助你理解golang以及python的風(fēng)格用法特點(diǎn),這里給出參考:

class CacheFib(object):

    def __init__(self):
        self.cache = dict()
        self.f = None

    def wrap(self, obj):
        self.f = obj.fib
        # return self

    def fib(self, n):
        if not self.cache.get(n):
            self.cache[n] = self.f(n)

        return self.cache[n]


class CountFib(object):

    def __init__(self):
        self.count = 0
        self.f = None

    def wrap(self, obj):
        self.f = obj.fib
        # return self

    def fib(self, n):
        self.count += 1
        return  self.f(n)


class Fib(object):

    def __init__(self):
        self.f = self.fib

    def wrap(self, obj):
        self.f = obj.fib
        # return self

    def fib(self, n)->int:
        if n == 0:
            return 0
        elif n == 1:
            return 1
        else:
            return self.f(n-1) + self.f(n-2)


if __name__ == '__main__':
    f = Fib()
    cache = CacheFib()
    count = CountFib()

    f.wrap(cache)
    count.wrap(f)
    cache.wrap(count)

    # cache = cache.wrap(count.wrap(f.wrap(cache))
    cache.fib(20)
    print("count: {}, cache: {}".format(count.count, cache.cache) )
最后編輯于
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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