0%

Some thoughts about duck typing

再不寫點東西,這邊就長草了
這幾天在看go-redis專案,顧名思義就是在golang當中對redis操作的程式庫

其中有一段程式碼是這樣

1
2
3
4
5
6
func ExampleClient() {
err := client.Set("key", "value", 0).Err()
if err != nil {
panic(err)
}
}

結果去redis.go裡面查看,找不到Set這個函數的實作
只好用grep去找哪邊可能實作這個函數
最後讓我在command.go找到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func (c *cmdable) Set(key string, value interface{}, expiration time.Duration) *StatusCmd {
args := make([]interface{}, 3, 4)
args[0] = "set"
args[1] = key
args[2] = value
if expiration > 0 {
if usePrecise(expiration) {
args = append(args, "px", formatMs(expiration))
} else {
args = append(args, "ex", formatSec(expiration))
}
}
cmd := NewStatusCmd(args...)
c.process(cmd)
return cmd
}

在同一個檔案中找到cmdable的定義

1
2
3
type cmdable struct {
process func(cmd Cmder) error
}

回頭看我們的redis.go,發現一樣的東西

1
2
3
4
type baseClient struct {
// Ignore unrelated fields
process func(Cmder) error
}

因為有同樣的Singature,所以可以把baseClient當cmder`來用

所以?

雖然找到了我想要的答案,不過我不喜歡這方法
由於我找不到Set這函數,於是我需要grep找到可能的實作 => 發現baseClient和cmadble的相似處
那為什麼不直接用繼承關係就好了,這樣可以找到相依性

1
2
3
4
5
6
struct ICmdable {
virtual std::error process(...) = 0;
};
struct baseClient : ICmdable {
std::error process(...) override;
};

這樣可以看出baseClient必須繼承’ICmdable’這個介面
不過可能引申出多重繼承的問題,老話一句,沒有什麼方法一體適用