讀代碼之golang裝飾器模式

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

代碼如下:

// 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 調用", 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)
}

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

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

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

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

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

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

不明白的話就運行一遍代碼:
斐波那契數(shù)列計算完美,有計算次數(shù),也優(yōu)化了計算邏輯(緩存了)
但這并不妨礙咱們的懵逼狀態(tài)ing~
整個方法的調用鏈是怎樣的呢

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

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

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

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

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

友情鏈接更多精彩內容